Nessus RESTful API — проблемы Java с реализацией POST

В настоящее время мне поручено автоматизировать Nessus.

В то время как почти все работает нормально, я кусаю камень, работая с этим вызовом (из 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.

я проверил вызов(ы) с помощью CURL, который отлично работает:

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"

... который возвращает предполагаемый результат:

{"scan_uuid":"06c4aed8-ee64-c44e-9800-f6aeed1ba94fab8b2ed9c1033626"}

Теперь к сути проблемы: делаем то же самое на Java!

Я получаю следующее:

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)

мой метод postRequest выглядит так (и работает с другими вызовами!):

/**
 * 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);
}

... теперь я подозреваю, что вывод postData: alt_targets=%27127.0.0.1%27&scan_id=21 недействителен. Хотя я понятия не имею (и Интернет дает удивительно мало информации) о том, что такое «массив» в контексте запроса POST и как он кодируется в POST. В CURL это работает отлично, а в Java - нет, несмотря на то, что этот метод подходит для других вызовов API (создание сеансов и получение токена работает с тем же методом).

Вот вызывающая часть кода:

/* 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();
    }

API-Docu тоже мало помогает, как вы можете видеть выше.

Вопросы:

  • как правильно отправить значения «массива» в запросе POST?
  • как я могу увидеть/отладить/проверить, что на самом деле отправлено/как выглядит POST?
  • как исправить мой скрипт?

Благодарю вас!


person Gewure    schedule 21.06.2017    source источник


Ответы (1)


Я наконец исправил это!

Проблема заключалась в формате строки данных, которую я отправил в качестве полезной нагрузки. Это не было задокументировано, но API работает только с JSON-запросами. По совпадению мой первый POST-запрос (/session) был действительным JSON, а второй (/scans/{id}/launch) — нет.

поэтому выполнение действительного JSON в качестве полезной нагрузки данных POST помогло:

    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();
    }

... что приводит к допустимой полезной нагрузке данных JSON POST: {"scan_id":"21", "alt_targets":["127.0.0.1"]}

...вместо старого: scan_id=21&alt_targets=[10.208.65.226]

person Gewure    schedule 21.06.2017