RITSEC CTF 2019 - Write-up

Welcome to my blog!

Here is my write-up for some very cool challenges in RITSEC CTF 2019 that I solved last weekend. Hope you enjoy it!

Our First API

ctfchallenges.ritsec.club:3000 ctfchallenges.ritsec.club:4000
Hint: You don't need the Bearer keyword!
Author: sandw1ch
Go to ctfchallenges.ritsec.club:4000, you can see what we need to do to get the flag.
We should get a token at ctfchallenges.ritsec.club:3000/auth, then use it to authenticated with the /api/admin endpoint. Oh, I guess it's a JWT challenge!
If you are unfamiliar with exploiting JWT, you shoud visit here first.

First, let's get the token.

After that, I use jwt.io to decode it's data. Wow, it uses the RS256 algorithm. Maybe you know what is our next step? Yeah, we only need to find the public key and then sign our modified data with that key and the HS256 algorithm.

Take a look at ctfchallenges.ritsec.club:3000/robots.txt, we can see the file signing.pem, which is certainly the publickey that the server used to sign the token.
-----END PUBLIC KEY-----

Now, it's very simple. Here is my Python script to generate a new valid token with the type is admin.
import jwt
  "name": "abc",
  "type": "admin",
  "iat": 1573986641
print jwt.encode(e,s,algorithm='HS256')

Let's use the admin's token to access the /api/admin endpoint and get the flag. It's RITSEC{JWT_th1s_0ne_d0wn}.

Hop By Hop

Hint: I thought I would forward this hint to you.
Author: Chaim Sanders 
If you often read infosec news, you could realize the challenge's name. It's "Abusing HTTP hop-by-hop request headers". The article was shared many times on Twitter, Reddit,...

Look at the challenge's site, it has an admin panel. On this page, a message is showed that only the admin's computer can access it. Furthermore, the request's IP is also shown and it's actually the load balancer's IP (or the first proxy),, not our IP.

As you know, the load balancer adds its IP before sending the request to the web server by adding the header X-Forwarded-For. So, what happens if we specify that header from our computer?

Even when the header X-Forwarded-For contains the IP, we still can't access the admin page. At that time, I think maybe the admin's computer is in the local network with the web server so its requests don't need to have X-Forwarded-For. Let's use the header Connection to force the proxy strips off X-Forwarded-For before forwarding requests to the web server.

The flag needs to be surrounded by RITSEC{}. Here it is: RITSEC{2227DF03559A4C4E1173BF3565964FD3}
Do you think it's much simpler than the challenge's point looks like?


One of our operatives sent us this packet capture but we aren't quite sure what to make of it, what can you find?
Author: DataFrogman
Open the pcap file, you can see that all packets are USB packets.

Look at the captured data, it's in the format of 8 bytes with the first byte is 00, 01 or 02, the second byte is 00 and the other bytes seems to be random. It indicates that this is keyboard traffic. Let's extract the capture data by tshark:
tshark -r URGGGGGG.pcapng -T fields -e usb.capdata > data.txt

To convert the captured data to the symbol of pressed keys, I wrote a simple Python script like that:
 "01": "[MOD_LCTRL]", "02": "[MOD_LSHIFT]", "04": "[MOD_LALT]", "08": "[MOD_LMETA]", "10": "[MOD_RCTRL]", "20": "[MOD_RSHIFT]", "40": "[MOD_RALT]",
 "80": "[MOD_RMETA]", "00": "[NONE]", "01": "[ERR_OVF]", "04": "aA", "05": "bB", "06": "cC", "07": "dD", "08": "eE", "09": "fF", "0a": "gG",
 "0b": "hH", "0c": "iI", "0d": "jJ", "0e": "kK", "0f": "lL", "10": "mM", "11": "nN", "12": "oO", "13": "pP", "14": "qQ", "15": "rR", "16": "sS",
 "17": "tT", "18": "uU", "19": "vV", "1a": "wW", "1b": "xX", "1c": "yY", "1d": "zZ", "1e": "1!", "1f": "2@", "20": "3#", "21": "4$", "22": "5%",
 "23": "6^", "24": "7&", "25": "8*", "26": "9(", "27": "0)", "28": "[ENTER]", "29": "[ESC]", "2a": "[BACKSPACE]", "2b": "[TAB]", "2c": " ",
 "2d": "-_", "2e": "=+", "2f": "[{", "30": "]}", "31": "\\|", "32": "[HASHTILDE]", "33": ";:", "34": "'\"", "35": "[GRAVE]", "36": ",<",
 "37": ".>", "38": "/?", "39": "[CAPSLOCK]", "3a": "[F1]", "3b": "[F2]", "3c": "[F3]", "3d": "[F4]", "3e": "[F5]", "3f": "[F6]", "40": "[F7]",
 "41": "[F8]", "42": "[F9]", "43": "[F10]", "44": "[F11]", "45": "[F12]", "46": "[SYSRQ]", "47": "[SCROLLLOCK]", "48": "[PAUSE]", "49": "[INSERT]",
 "4a": "[HOME]", "4b": "[PAGEUP]", "4c": "[DELETE]", "4d": "[END]", "4e": "[PAGEDOWN]", "4f": "[RIGHT]", "50": "[LEFT]", "51": "[DOWN]",
 "52": "[UP]", "53": "[NUMLOCK]", "54": "[KPSLASH]", "55": "[KPASTERISK]", "56": "[KPMINUS]", "57": "[KPPLUS]", "58": "[KPENTER]", "59": "[KP1]",
 "5a": "[KP2]", "5b": "[KP3]", "5c": "[KP4]", "5d": "[KP5]", "5e": "[KP6]", "5f": "[KP7]", "60": "[KP8]", "61": "[KP9]", "62": "[KP0]",
 "63": "[KPDOT]", "64": "[102ND]", "65": "[COMPOSE]", "66": "[POWER]", "67": "[KPEQUAL]", "68": "[F13]", "69": "[F14]", "6a": "[F15]",
 "6b": "[F16]", "6c": "[F17]", "6d": "[F18]", "6e": "[F19]", "6f": "[F20]", "70": "[F21]", "71": "[F22]", "72": "[F23]", "73": "[F24]",
 "74": "[OPEN]", "75": "[HELP]", "76": "[PROPS]", "77": "[FRONT]", "78": "[STOP]", "79": "[AGAIN]", "7a": "[UNDO]", "7b": "[CUT]", "7c": "[COPY]",
 "7d": "[PASTE]", "7e": "[FIND]", "7f": "[MUTE]", "80": "[VOLUMEUP]", "81": "[VOLUMEDOWN]", "85": "[KPCOMMA]", "87": "[RO]",
 "88": "[KATAKANAHIRAGANA]", "89": "[YEN]", "8a": "[HENKAN]", "8b": "[MUHENKAN]", "8c": "[KPJPCOMMA]", "90": "[HANGEUL]", "91": "[HANJA]",
 "92": "[KATAKANA]", "93": "[HIRAGANA]", "94": "[ZENKAKUHANKAKU]", "b6": "[KPLEFTPAREN]", "b7": "[KPRIGHTPAREN]", "e0": "[LEFTCTRL]",
 "e1": "[LEFTSHIFT]", "e2": "[LEFTALT]", "e3": "[LEFTMETA]", "e4": "[RIGHTCTRL]", "e5": "[RIGHTSHIFT]", "e6": "[RIGHTALT]", "e7": "[RIGHTMETA]",
 "ed": "[MEDIA_VOLUMEUP]", "ee": "[MEDIA_VOLUMEDOWN]", "ef": "[MEDIA_MUTE]", "f0": "[MEDIA_WWW]", "f1": "[MEDIA_BACK]", "f2": "[MEDIA_FORWARD]",
 "f3": "[MEDIA_STOP]", "f4": "[MEDIA_FIND]", "f5": "[MEDIA_SCROLLUP]", "f6": "[MEDIA_SCROLLDOWN]", "f7": "[MEDIA_EDIT]", "f8": "[MEDIA_SLEEP]",
 "f9": "[MEDIA_COFFEE]", "fa": "[MEDIA_REFRESH]", "fb": "[MEDIA_CALC]"

for i in f:
 if k=='00':
 if codes[k][0]=='[' and codes[k][-1]==']':
  if i[0:2]=='02':
  elif i[0:2]=='01':
 if len(s)>1 and s[-1]==s[-2]:
print ''.join(s)

After running the script above, all pressed keys are revealed:
ablglaolgalfkihikfhwhjd;s[ENTER]jofs[ENTER]alehwaih)($439(YTY$495[F1]afughw8o[BACKSPACE][ENTER][DELETE][ENTER][BACKSPACE] [ENTER][BACKSPACE][ENTER][DELETE] [BACKSPACE]RITSEC[ESC]{wH0_s@[ESC][UP][LEFT][DOWN][LEFT][UP][LEFT][CTR]xx[DOWN][RIGHT][CTR]vvd_n[UP][RIGHT][LEFT][CTR]c[DOWN][CTR]vtw0rk1nG_wAs[ENTER]jojlgajkgajkselfdje[UP]_tH3_oNlY_pAck3t_TyP3}[DOWN][ENTER]auakg;ekajildhkljaskdgafsfekjkjfcvhihelshiheueuhdhoa

What makes this challenge different from other USB forensics challenge is the existence of Ctr+C, Ctr+X, and Ctr+V. However, you can easily guess what are copied and pasted by looking at the flag's style. Yeah, it's leet!
So the flag is: RITSEC{wH0_s@id_n3tw0rk1nG_wAs_tH3_oNlY_pAck3t_TyP3}

If you have any questions, please don't hesitate to ask me on Twitter or leave a comment.
Thank you for reading!