Im currently tasked to do automation for Nessus.
While almost everything works fine, im biting on rock working with this call (from API-DOCU):
Request
HTTP Request
POST /scans/{scan_id}/launch
Parameters
scan_id integer The id of the scan to launch.
alt_targets array If specified, these targets will be scanned instead of the default. Value can be an array where each index is a target, or an array with a single index of comma separated targets.
Response
Status Code Description
200 Returned if the scan was successfully launched.
403 Returned if the scan is disabled.
404 Returned if the scan does not exist.
i've tested the call(s) with CURL, which works fine:
curl -X POST -H 'X-Cookie: token=db565871198eec7fd9569dd1e3ffb8b2a60f757329749bc5' -H 'Content-Type:application/json' --data '{"scan_id":"21", "alt_targets":[127.0.0.1]}' -k "https://nessusServer:8834/scans/21/launch"
...which gives back the intended result:
{"scan_uuid":"06c4aed8-ee64-c44e-9800-f6aeed1ba94fab8b2ed9c1033626"}
Now to the heart of the Problem: Doing the Same in Java!
What i get is this:
java.io.IOException: Server returned HTTP response code: 400 for URL: https://nessusServer:8834/scans/21/launch
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at sun.net.www.protocol.http.HttpURLConnection$10.run(HttpURLConnection.java:1890)
at sun.net.www.protocol.http.HttpURLConnection$10.run(HttpURLConnection.java:1885)
at java.security.AccessController.doPrivileged(Native Method)
at sun.net.www.protocol.http.HttpURLConnection.getChainedException(HttpURLConnection.java:1884)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1457)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1441)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:254)
at at.bachmann.se.security.NessusAPI.postRequest(NessusAPI.java:466)
my postRequest Method looks like this (and it works with other calls!):
/**
* Sends a post Request
*
* @param urlPathAdditional
* .. the added part of the path (e.g. /scans/{scanID} )
* @param headers
* .. Map<String, String> the Request Properties
*
* @return Response ... Response-Clazz containing String and Code
* @throws UnsupportedEncodingException
*/
public Response postRequest(String urlPathAdditional, Map<String, String> headers) throws Exception {
System.out.println("postRequest()......");
StringJoiner sj = new StringJoiner("&");
for (Map.Entry<String, String> entry : headers.entrySet())
sj.add(URLEncoder.encode(entry.getKey(), "UTF-8") + "=" + URLEncoder.encode(entry.getValue(), "UTF-8"));
String postData = sj.toString();
System.out.println("postData: " + sj.toString());
URL obj;
HttpsURLConnection con = null;
try {
obj = new URL(apiUrl + urlPathAdditional);
con = (HttpsURLConnection) obj.openConnection();
TrustModifier.relaxHostChecking(con); // here's where the magic happens: SSL is overrated! :)
con.setRequestMethod("POST");
} catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException |IOException e1) {
e1.printStackTrace();
}
//con.setRequestProperty("Content-Type", "application/json");
con.setRequestProperty("User-Agent", USER_AGENT);
con.setRequestProperty("Accept-Language", "en-US,en;q=0.5");
//con.setRequestProperty("Charset", "UTF-8");
//con.setRequestProperty("Content-Length", Integer.toString(postData.length()));
con.setRequestProperty("X-Cookie", "token=" + token);
con.setDoOutput(true);
int respCode = 0;
/* Send post request */
try {
DataOutputStream wr = new DataOutputStream(con.getOutputStream());
wr.writeBytes(postData);
wr.flush();
wr.close();
} catch (IOException e) {
e.printStackTrace();
}
respCode = con.getResponseCode();
/* read response */
BufferedReader in = new BufferedReader(
new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
return new Response(response.toString(), respCode);
}
...now what i suspect is that the output postData: alt_targets=%27127.0.0.1%27&scan_id=21
isn't valid.
Though i have no idea (and the internet gives astonishingly few informations) about what an "array" is in context of a POST request - and how it is encoded into a POST. In CURL it works perfectly - in Java it does not, despite the method being OK for other API-Calls (creating a sessions and retrieving the token works with the same method).
Here the calling part of the code:
/* at this point the server is ready */
/* so we need to get the ID of the scan-name we want to launch */
int scanId = getScanIdForName(terminalOrM1 + scanType);
/* Scanner is Ready for a new Scan! */
// 200 Returned if the scan was successfully launched.
// 403 Returned if the scan is disabled.
// 404 Returned if the scan does not exist.
String query = "scans/" + scanId + "/launch";
String targets = "\'" + ip + "\'"; // <= DOESN'T WORK
//String target = ip; // DOESN'T WORK EITHER -- so what does?
//String target = "[" + ip + "]"; // NO WORK
Map<String, String> headers = new HashMap<>();
headers.put("alt_targets", targets);
headers.put("scan_id", String.valueOf(scanId));
/* launch it! */
Response respLaunch = null;
try {
respLaunch = postRequest(query, headers);
} catch (Exception e) {
e.printStackTrace();
}
The API-Docu doesn't help much either, as you can see above.
Questions:
Thank you!
I finally fixed it!
The problem was with the format of the data-String that i sent as payload. It was not documented, but the API works with JSON-Requests only. Coincidentally my first POST-Request ( /session ) was valid JSON, while the second ( /scans/{id}/launch) wasn't.
so doing valid JSON as POST data-payload did the trick:
String query = "scans/" + scanId + "/launch";
String launchJson = "{\"scan_id\":\"" +String.valueOf(scanId) + "\", \"alt_targets\":[\"" + ip +"\"]}";
/* launch it! */
Response respLaunch = null;
try {
respLaunch = postRequest(query, launchJson);
} catch (Exception e) {
e.printStackTrace();
}
...which results in a valid JSON POST data-payload: {"scan_id":"21", "alt_targets":["127.0.0.1"]}
...instead of the old one: scan_id=21&alt_targets=[10.208.65.226]