Installing software by piping from curl to bash is obviously a bad idea and a knowledgable user will most likely check the content first. So wouldn’t it be great if a malicious payload would only render when piped to bash? A few people have tried this before by checking for the curl user agent which is by no means fail safe – the user may simply curl the url on the commandline revealing your malicious code. Luckily the behaviour of curl (and wget) changes subtely when piped into bash. This allows an attacker to present two different versions of their script depending on the context :)
Its not that the HTTP requests from curl when piped to bash look any different than those piped to stdout, in fact for all intents and purposes they are identical:
# curl -vv http://pluver.xqi.cc/setup.bash * Hostname was NOT found in DNS cache * Trying 69.28.82.189... * Connected to xqi.cc (69.28.82.189) port 80 (#0) > GET /setup.sh HTTP/1.1 > User-Agent: curl/7.35.0 > Host: xqi.cc > Accept: */* >
The key difference is in time it takes for the contents of large http responses to be ingested by bash.
Passive detection using a short delay
Execution in bash is performed line by line and so the speed that bash can ingest data is limited by the speed of execution of the script. This means if we return a sleep at the start of our script the TCP send stream will pause while we wait for the sleep to execute. This pause can be detected and used to render different content streams.
Unfortuneatly its not just a simple case of wrapping a socket.send(“sleep 10”) in a timer and waiting for a send call to block. The send and receive TCP streams in linux are buffered on a per socket basis, so we have to fill up these buffers before the call to send data will block. We know the buffer is full when the receiving client to replies to a packet with the Window Size flag set to 0 (Win=0 in wireshark).
Filling the TCP buffers
To detect a pause in execution we need to fill