vladko312 / SSTImap

Automatic SSTI detection tool with interactive interface
GNU General Public License v3.0
775 stars 91 forks source link

Feature for PUT method, data sent in JSON format, and has verbose #28

Closed alasalamont closed 8 months ago

alasalamont commented 9 months ago

As the title does, I did try run

python3 sstimap.py -u "http://api.example.com" -m PUT -d "{\"username\":\"david",\"content\":\"*\"}" 

or

python3 sstimap.py -u "http://api.example.com" -m PUT -d "username=david&content=*" 

But none of them work. The issue might arise from the current version not supporting the PUT method, or not accepting data in JSON format, or am I using the tool incorrectly?

Beside that, another case successfully injected SSTI payload. In the result says File write: ok. So this means the tool can upload file on Target's server or the tools can run payload that can write file?

I also hope in the future the result can show which payloads are used during the scan, which payloads work.

Regards!

vladko312 commented 9 months ago

Currently, SSTImap is not supporting JSON. I am working on it, so hopefully I would be able to release an update soon enough.

alasalamont commented 9 months ago

It seems current version does not support with PUT request too. So far only support GET & POST method. Hope in the next update will have them all. Thanks bro

vladko312 commented 9 months ago

SSTImap fully supports PUT requests, as well as any other method (even nonexisting ones). For now, only application/x-www-form-urlencoded body is supported, so json data would get processed in the wrong way

alasalamont commented 9 months ago

SSTImap fully supports PUT requests, as well as any other method (even nonexisting ones). For now, only application/x-www-form-urlencoded body is supported, so json data would get processed in the wrong way

Then I suggest changing the output according to the -m REQUEST_TYPE, because when I test PUT request, the tool still says [*] Testing if POST parameter 'email' is injectable which can cause confused

vladko312 commented 9 months ago

Then I suggest changing the output according to the -m REQUEST_TYPE, because when I test PUT request, the tool still says [*] Testing if POST parameter 'email' is injectable which can cause confused

I would change it soon, as it is indeed confusing. It should clarify that it is a param in a body form

vladko312 commented 8 months ago

Should be fixed in 1.2.0 Can you verify?

Payloads should appear in ~/.sstimap/sstimap.log Verbose output might be added later

alasalamont commented 8 months ago

Should be fixed in 1.2.0 Can you verify?

Payloads should appear in ~/.sstimap/sstimap.log Verbose output might be added later

Hi brother, I did use version 1.2.0

And the POST request looks like

POST /api/pug HTTP/1.1

Host: template-sandbox

User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/json;charset=utf-8
Content-Length: 59
Origin: http://template-sandbox
Connection: close
Referer: http://template-sandbox/pug

{"data":"123","vars":{"showSecret":true,"name":"Osku"}}

Attempt 1

Here is the command that I run

python3 sstimap.py -u http://template-sandbox/api/pug -m POST -d "{\"data\":\"*\",\"vars\":{\"showSecret\":true,\"name\":\"Osku\"}}" --date-type json -e pug

And here is the tool response

sstimap.py: error: unrecognized arguments: --date-type json

Attempt 2

I ignore the flag --data-type, and run the tool runs but does not give the correct output

[*] Version: 1.2.0
[*] Author: @vladko312
[*] Based on Tplmap
[!] LEGAL DISCLAIMER: Usage of SSTImap for attacking targets without prior mutual consent is illegal.
It is the end user's responsibility to obey all applicable local, state and federal laws.
Developers assume no liability and are not responsible for any misuse or damage caused by this program
[*] Loaded plugins by categories: languages: 5; legacy_engines: 2; engines: 17
[*] Loaded request body types: 4

[*] Scanning url: http://template-sandbox/api/pug
[*] Testing if Body parameter '{"data":"*","vars":{"showSecret":true,"name":"Osku"}}' is injectable
[*] Pug plugin is testing rendering with tag '\n= *\n'
[*] Pug plugin is testing )*// code context escape with 6 variations
[*] Pug plugin is testing blind injection
[*] Pug plugin is testing )*// code context escape with 6 variations
[-] Tested parameters appear to be not injectable.

So did I use the tool wrongly?

I also expect you can provide the value for each flags. Assume user does not know what value of the flag can be input, user can run

python3 sstimap.py --data-type -h

And the tool will shows all the value that flag --data-type accepts. Then it will be easier to use. To be honest, there are some flags that I dont know how to use/ or when to use such as --load-forms, what is the different between -l and -L

vladko312 commented 8 months ago

In your attempt 1 you used --date-type instead of --data-type, which caused the error. In attempt 2 SSTIMAP tried processing JSON as a web form, which prevented it from finding vulnerabilities.

I agree that SSTImap needs better documentation. I will work on that later. As for params you mentioned:

alasalamont commented 8 months ago

In your attempt 1 you used --date-type instead of --data-type, which caused the error. In attempt 2 SSTIMAP tried processing JSON as a web form, which prevented it from finding vulnerabilities.

I agree that SSTImap needs better documentation. I will work on that later. As for params you mentioned:

  • --load-forms is a way to load forms saved using --save-forms
  • -l tries all context escapes up to the selected LEVEL
  • -L tries template context escapes only for the selected LEVEL and base language context escapes only for the selected CLEVEL. I rarely use the last flag myself. It was probably added to Tplmap to skip tests after the initial detection to allow faster exploitation. In SSTImap I made fast exploitation easier adding interactive mode (-i), so restarting detection is no longer needed.

Hi thank you for pointing out my typo. I did test again,

python3 sstimap.py -u "http://template-sandbox/api/pug" -m POST -d "{\"data\":\"123*\",\"vars\":{\"showSecret\":true,\"name\":\"Osku\"}}" --data-type json --proxy http://127.0.0.1:8080 -e pug

and here is the result

[*] Version: 1.2.0
[*] Author: @vladko312
[*] Based on Tplmap
[!] LEGAL DISCLAIMER: Usage of SSTImap for attacking targets without prior mutual consent is illegal.
It is the end user's responsibility to obey all applicable local, state and federal laws.
Developers assume no liability and are not responsible for any misuse or damage caused by this program
[*] Loaded plugins by categories: languages: 5; legacy_engines: 2; engines: 17
[*] Loaded request body types: 4

[*] Scanning url: http://template-sandbox/api/pug
[*] Testing if Body parameter 'data' is injectable
[*] Pug plugin is testing rendering with tag '\n= *\n'
[*] Pug plugin is testing )*// code context escape with 6 variations
[*] Pug plugin is testing blind injection
[*] Pug plugin is testing )*// code context escape with 6 variations
[-] Tested parameters appear to be not injectable.

I also check the POST request via Burp. It seems the tool try to add \n that cause the payload does not work as expect

POST /api/pug HTTP/1.1
Host: template-sandbox
User-Agent: SSTImap/1.2.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: close
Content-Length: 116

{"data": "123\n= 7401602210\n\n= typeof(88)+69\n\n= 9646249855\n", "vars": {"showSecret": true, "name": "Osku"}}

And here is the response

HTTP/1.1 500 Internal Server Error
Content-Length: 1152
Content-Security-Policy: default-src 'none'
Content-Type: text/html; charset=utf-8
Date: Sun, 07 Jan 2024 16:13:43 GMT
X-Content-Type-Options: nosniff
X-Powered-By: Express
Connection: close

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>TypeError: Cannot set property &#39;SECRET_STRING&#39; of undefined<br> &nbsp; &nbsp;at /usr/src/app/index.js:14:26<br> &nbsp; &nbsp;at Layer.handle [as handle_request] (/usr/src/app/node_modules/express/lib/router/layer.js:95:5)<br> &nbsp; &nbsp;at next (/usr/src/app/node_modules/express/lib/router/route.js:137:13)<br> &nbsp; &nbsp;at Route.dispatch (/usr/src/app/node_modules/express/lib/router/route.js:112:3)<br> &nbsp; &nbsp;at Layer.handle [as handle_request] (/usr/src/app/node_modules/express/lib/router/layer.js:95:5)<br> &nbsp; &nbsp;at /usr/src/app/node_modules/express/lib/router/index.js:281:22<br> &nbsp; &nbsp;at Function.process_params (/usr/src/app/node_modules/express/lib/router/index.js:335:12)<br> &nbsp; &nbsp;at next (/usr/src/app/node_modules/express/lib/router/index.js:275:10)<br> &nbsp; &nbsp;at urlencodedParser (/usr/src/app/node_modules/body-parser/lib/types/urlencoded.js:100:7)<br> &nbsp; &nbsp;at Layer.handle [as handle_request] (/usr/src/app/node_modules/express/lib/router/layer.js:95:5)</pre>
</body>
</html>

Regards!

vladko312 commented 8 months ago

I also check the POST request via Burp. It seems the tool try to add \n that cause the payload does not work as expect

Have you tried resending the request through burp repeater without extra \n?

alasalamont commented 8 months ago

I did find out the reason. If I add -H "Content-Type: application/json;charset=utf-8" it works perfectly. So I think the tool should remind user or add for user this header when use option --data-type json

By the way, what is the char key that combine multiple Headers? or stack multiple Cookies?

I did use semi-colon to stack multiple headers but it does not work. Example: -H "Content-Type: application/json;charset=utf-8; Host: template-sandbox it turn out in POST request: Content-Type: application/json;charset=utf-8, Host:template-sandbox. They are not separate in new line

vladko312 commented 8 months ago

So I think the tool should remind user or add for user this header when use option --data-type json

Yes, this is a good idea.

By the way, what is the char key that combine multiple Headers? or stack multiple Cookies?

The intended way of specifying multiple headers/cookies is by using multiple -H/-C flags or corresponding interactive commands using -i. If really needed, headers can be separated within one flag by \r\n and cookies by ;, just like in HTTP.

alasalamont commented 8 months ago

But I am not sure this is work for all cases or not. To me, it is still kind of weird as follow

If I send your payload \n= 7530515504\n\n= typeof(91)+96\n\n= 5816490115\n in via Burp, it works

But if I inject directly on GUI website, the site response There was an error rendering your content

Error: Pug:1:1
  > 1| \n= 7530515504\n\n= typeof(91)+96\n\n= 5816490115\n
-------^
unexpected text "\n= 2"
    at makeError (/usr/src/app/node_modules/pug-error/index.js:34:13)
    at Lexer.error (/usr/src/app/node_modules/pug-lexer/index.js:62:15)
    at Lexer.fail (/usr/src/app/node_modules/pug-lexer/index.js:1629:10)
    at Lexer.advance (/usr/src/app/node_modules/pug-lexer/index.js:1694:12)
    at Lexer.callLexerFunction (/usr/src/app/node_modules/pug-lexer/index.js:1647:23)
    at Lexer.getTokens (/usr/src/app/node_modules/pug-lexer/index.js:1706:12)
    at lex (/usr/src/app/node_modules/pug-lexer/index.js:12:42)
    at Object.lex (/usr/src/app/node_modules/pug/lib/index.js:104:9)
    at Function.loadString [as string] (/usr/src/app/node_modules/pug-load/index.js:53:24)
    at compileBody (/usr/src/app/node_modules/pug/lib/index.js:82:18)

Somehow your payload only works in Burp, not in GUI site

Why dont you use simple payload such as #{111*"111"} to detect?

vladko312 commented 8 months ago

Somehow your payload only works in Burp, not in GUI site

Payload is transfered in JSON, so newlines are encoded as \n. In Burp, you send this encoded payload, so it works fine. While sending it through GUI, you are sending \n as two literal charecters, as it is encoded again in JSON becoming \\n and breaking the syntax. To send payload through GUI, try just replacing \n with actual newlines.

Why dont you use simple payload such as #{111*"111"} to detect?

First of all, we need to escape to template context for our payload to work. In the simplest cases, we are already there, but we need to check other cases up to the selected --level (for example, #{"Hello, NICKNAME!"} would require something like "}PAYLOAD#{"). Then, to check the result, we need to add some ways of finding our result on the page. This is what the big numbers in the payload are for. Also, there are many engines with a very similar basic syntax but different exploitation payloads, so #{111*"111"} will not be enough to check if we have found the right engine. As a result, detection payloads are a bit more complex, using unique features of the languages, such as multiline syntax of Pug and typeof(91)+96 evaluating to number96 in javascript. Exploitation payloads are way more complex, as they are designed to work with any higher-level payload passed through them.

I have plans to add a faster detection method based on syntax detection, but it would add some time.

alasalamont commented 8 months ago

Somehow your payload only works in Burp, not in GUI site

Payload is transfered in JSON, so newlines are encoded as \n. In Burp, you send this encoded payload, so it works fine. While sending it through GUI, you are sending \n as two literal charecters, as it is encoded again in JSON becoming \\n and breaking the syntax. To send payload through GUI, try just replacing \n with actual newlines.

Why dont you use simple payload such as #{111*"111"} to detect?

First of all, we need to escape to template context for our payload to work. In the simplest cases, we are already there, but we need to check other cases up to the selected --level (for example, #{"Hello, NICKNAME!"} would require something like "}PAYLOAD#{"). Then, to check the result, we need to add some ways of finding our result on the page. This is what the big numbers in the payload are for. Also, there are many engines with a very similar basic syntax but different exploitation payloads, so #{111*"111"} will not be enough to check if we have found the right engine. As a result, detection payloads are a bit more complex, using unique features of the languages, such as multiline syntax of Pug and typeof(91)+96 evaluating to number96 in javascript. Exploitation payloads are way more complex, as they are designed to work with any higher-level payload passed through them.

I have plans to add a faster detection method based on syntax detection, but it would add some time.

Thank you for enlighting me up <3 now I understand