mushorg / tanner

He who flays the hide
GNU General Public License v3.0
220 stars 101 forks source link

JSON Logging hierarchy issue #405

Open adskee opened 3 years ago

adskee commented 3 years ago

Not sure if it's intentional or not, but based on the text used by an attacker in the POST data, categories can get created when saving entries into JSON.

Example: This snippet of an HTTP request:

POST /cgi-bin/ ... etc. HTTP/1.1 -other headers- Connection: Keep-Alive

< ? php die(@md5(PHP-CGI)); ?>


Gets saved into JSON as:

"post_data": { "< ? php die(@md5(PHP-CGI))": "", " ?>": "" }"

where the semicolon in the attacker's post data became the delimeter. So when running through a JSON parser, you can end up with fields such as "post_data. < ? php die(@md5(PHP-CGI))" and "post_data. ?>" which have no values associated.

I think the logger should be saving this kind of attack data whole, perhaps in a blank field name (since it's not in a field=value format). I'd think it's possible to write a parser to reconstruct things like this, but it'd definitely be easier if it wasn't split among fields like this.

Hopefully that's clear - let me know if I need to clarify / provide some data or screenshots. Happy to help! Edit - had to space out the < ? php so the text shows and the textbox wasn't actually interpreting php... lol.

afeena commented 3 years ago

Hi @adskee, thanks for reporting this! That is not intended indeed. If you have any suggestions on how it can be fixed, please feel free to submit the pull request :)

adskee commented 3 years ago

Hi @afeena ! This actually might be pretty over my head. I've been flipping through multiple pages of code trying to understand Tanner, aiohttp, and the json packages. I don't know enough to directly suggest a fix; however, I think I found where a fix could go.

tanner/tanner/server.py

Line 57: data = json.loads(data.decode('utf-8'))

However data is formatted (from data=request.read()), the json parser in the json.loads() function reads it into json format. Potentially you could check after that if the post_data key has a sub dictionary or just one value, and if it's the former, transforming it into one value.