首页
社区
课程
招聘
[原创]pwn—栈的学习
发表于: 2024-7-18 16:46 7317

[原创]pwn—栈的学习

2024-7-18 16:46
7317

文章基于台科大LJP的pwn课程学习
https://www.youtube.com/watch?v=8zO47WDUdIk&t=1087s

与c语言比较

rax和rbx是x86架构下的一种通用寄存器

image-20240317193730112

image-20240317193903917

jmp这里是跳过的意思,这里指直接跳到begin

cmp是比较的意思

jle是conditional jump指条件跳转(即如果前一个比较指令结果表明第一个操作数小于或等于第二个操作数,则执行跳转)

LOOP这里指的是直接跳出去(这里是跳到循环开头)

如果要搜索直接在谷歌或者edge输入==x86 要搜索的指令==

在 x86 架构中,特别是在函数调用过程中,通常会使用基址指针 bp(也称为帧指针)来访问函数的局部变量和参数

当一个函数被调用时,它的栈帧(stack frame)被压入栈中,bp 指针会指向栈帧的基址,这样就可以通过 bp 指针来访问函数的局部变量和参数

read() 函数会尝试从文件描述符 fd 中读取 count 个字节的数据,并将数据存储到 buf 所指向的缓冲区中

它会返回实际读取的字节数:

read() 函数是一个阻塞函数,如果没有数据可读,它会一直等待

不同的区域存在不同的栈帧,里面存放不同的局部变量,每个function都有头部和尾部

头部:prologue

尾部:epilogue

前面是头部后面是尾部

image-20240317202312587

image-20240317202753822

0x0007fffffffe5c8是rsp的具体的位置

这个地方做一个说明这里的8bit是一个举例,在x86 架构下,这里一般是4个字节(32个bit)

image-20240317203837044

image-20240317204200395

image-20240317204809250

image-20240317205603743

image-20240317210011735

image-20240317210148972

image-20240317210254407

image-20240317210906662

这里不太好理解,

我认为的是这里的pop指令的关键就是上面的铺设和mov的操作的一次返还来保证这里的栈的稳定性,不发生偏移

1.取消掉rsp前面被push进入栈的8个bit也就是rbp原本的值

2.然后取消掉==这里rbp被mov到rsp经过push压栈后的值的操作==

结果如下(若果不理解可以再看看上面栈头部的操作)

image-20240317214938037

这里的执行方法就是说将这里rsp中的0x401234的返回值pop给一个另外的寄存器rip(它的作用是不断更新执行那个下一条将要执行的地址)这样rip就会将rsp的pop回到原来的值(也就是e5a0对应的值,也就是main函数那里leave对应的地址)

image-20240317221717108

这里可以发现stack的值已经返回了原来的函数调用前的值

取消掉rsp前面被push进入栈的8个bit也就是rbp原本的值

然后取消掉==这里rbp被mov到rsp经过push压栈后的值的操作==

这里推荐大佬的文章配置pwn的ubuntu的pwn环境

ubuntu20.04 PWN(含x86、ARM、MIPS)环境搭建_ubuntu powerpc编译环境-CSDN博客

但是个人觉得kali的好看一点

设定中断点

继续执行

执行一个命令

执行一个命令

image-20240318201347580

显示记忆内容

disassemble 命令用于反汇编指定的代码区域,将其转换为汇编语言的形式并显示出来

可以指定要反汇编的代码区域

使用指令示例:

disassemble

在不指定具体代码区域的情况下,会显示当前程序执行点所在的代码的汇编

disassemble function_name

指定函数名,显示该函数的汇编代码

disassemble start_address, end_address

指定地址范围,显示该范围内的代码的汇编

disassemble *address

指定具体的地址,显示该地址处的代码的汇编

这里是gef的显示的界面

image-20240319194213981

可以看见这里再b下断点之后发现,这里是展示了00到08的记忆体的数据

我么想要查看后面8位的数据

就使用指令

这里1个g就表示8个bit

就可以看到这个界面

image-20240319194611681

image-20240319194819296

用于创建本地进程。通过该函数,你可以启动一个本地的程序,并与其进行交互

其中 p 是一个 process 对象

/path/to/program 是要执行的程序的路径

你可以使用 p.send() 方法发送数据给该进程,使用 p.recv() 方法接收进程的输出

示例代码

用于创建网络连接并在远程主机上执行程序。通过该函数,你可以连接到远程主机的指定端口,并与其进行交互

r 是一个 remote 对象

host 是远程主机的 IP 地址或域名

port 是要连接的端口号

可以使用 r.send() 方法发送数据给远程主机,使用 r.recv() 方法接收远程主机的输出

示例代码

用于向进程或远程主机发送数据。无论是本地进程还是远程主机,你都可以使用 send 函数发送数据

其中 p 可以是 process 对象或 remote 对象,data 是要发送的数据

示例代码

发送带有换行符的数据。当需要发送一行数据并在末尾添加换行符时,可以使用 sendline 函数

其中 p 可以是 process 对象或 remote 对象

data 是要发送的数据,在末尾会自动添加换行符 \n

示例代码

在指定字符串出现后发送数据。当需要等待某个特定字符串出现后再发送数据时,可以使用 sendafter 函数

其中 p 可以是 process 对象或 remote 对象,search 是要搜索的字符串,一旦找到该字符串就会发送数据 "data"

示例代码

在指定字符串出现后发送带有换行符的数据。结合了 sendlinesendafter 的功能,在指定字符串出现后发送带有换行符的数据

其中 p 可以是 process 对象或 remote 对象,search 是要搜索的字符串,一旦找到该字符串就会发送带有换行符的数据 "data\n"

sendline 用于发送带有换行符的数

sendafter 用于在指定字符串出现后发送数据

sendlineafter 则是在指定字符串出现后发送带有换行符的数据

接收指定长度的数据。该函数用于接收指定长度的数据并返回

其中 p 可以是 process 对象或 remote 对象,n 是要接收的数据的长度。如果不指定 n,则会接收尽可能多的数据

接收一行数据。该函数用于接收一行数据并返回,一行数据以换行符 \n 结尾

示例代码

接收直到指定字符串出现为止的所有数据。该函数用于接收数据,直到指定的字符串出现为止,然后返回所有接收到的数据(包括指定的字符串)

其中 p 可以是 process 对象或 remote 对象,pattern 是要等待的字符串

后期慢慢说,现在知道概念就行

由在局部变量上面越界输入导致

依旧利用前面的栈帧来进行模拟

image-20240325175019692

假设这个蓝色的框里面存在使用者进行传参的窗口

如果传入的A没有限制

image-20240325175401795

image-20240325175436405

到leave的时候程序还能执行

但是到ret的时候,return回去8个A,就是8个4141的地址(假如它存在),如果不存在,程序就会出错

一种用于防范缓冲区溢出攻击的安全机制

会在程序的栈帧中插入一个称为"Stack Canary"的特殊值。这个值被设计为一个随机生成的数字,会在系统运行时动态地插入到程序栈的关键位置之间,如函数返回地址之前

当程序运行时,栈上的保护值会被监视,如果发生缓冲区溢出攻击,导致该值被修改,程序会检测到这种异常情况并采取相应的安全措施,例如终止程序执行,防止进一步的恶意操作

image-20240325194322222

fs是一个暂存器,这里fs:28h相当于将存储在 FS 段寄存器偏移地址为 0x28 的位置)加载到 RAX 寄存器

将rax的值放在rbp-8的位置

image-20240325195643959

image-20240325195757228

这里cannary的值被改掉了

mov rcx,[rbp-8]就是指尝试获取前面rbp+8存放的cannary的值

image-20240325200529067

image-20240325200513516

一样的输出0 ,如果不一样则结果不是0

image-20240325200728724

jump到ok的位置

image-20240325200900536

正常跳到ok,执行ok的后面正常退出

call __stack_chk_fail

呼叫这个检查函数,程序不会正常退出,程序检查到了栈溢出的问题

基本的想法:

泄露cannary的值

在传入参数的时候将cannnary放到正确的位置,其他还是一样的传入A

image-20240325202012354

就可以绕过检查

image-20240325202442198

不太清晰这个东西以后再分享类似的

一段用于利用计算机系统漏洞或实施攻击的机器码,通常是用来注入恶意代码并获得对系统控制权的一种技术。Shellcode 通常以二进制形式编写,用于利用特定的漏洞或弱点,例如缓冲区溢出漏洞。一旦成功执行,Shellcode 可能会启动一个 shell 或者执行其他恶意操作

image-20240325211219098

==这样本来的func1函数执行结束后应该返回位于main函数的该函数返回地址继续执行后面的操作,但是这里将地址更改后就会执行shellcode的程序==

image-20240325211624576

image-20240325212224010

image-20240325212347536

这里针对的就是刚刚的关键性步骤在栈上的数据,能够被当做指令执行的问题

栈上的记忆体有三重权限(rwx三重权限)

==r(read) w(write) x(eXcute)==

但是在设定nx之后将不会存在rwx区段

image-20240325214551615

在stack这个区段上面fde000-fff000存在rwx的权限

b main

之后run运行

到断点的位置

将会展示详细的记忆体区段信息

image-20240325215116165

这里可以发现这里的的字段存在了很多rwx的三重权限

一般来说这里都要可读r(否则无法读取的话执行和写入都无法进行)

将数据所在内存页(用户栈中)标识为不可执行

当程序溢出成功转入shellcode时,程序会尝试在数据页面上执行指令,此时CPU就会抛出异常,而不是去执行恶意指令

就像这样:

微信截图_20221014115435.png

image-20240327105141966

image-20240327105504550

image-20240327110140096

https://www.runoob.com/cprogramming/c-function-setvbuf.html

stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了一个打开的流

buffer -- 这是分配给用户的缓冲。如果设置为 NULL,该函数会自动分配一个指定大小的缓冲

mode -- 这指定了文件缓冲的模式:

size --这是缓冲的大小,以字节为单位

如果成功,则该函数返回 0,否则返回非零值

image-20240327114438698

image-20240327115120803

image-20240327115246671

image-20240327115405621

image-20240327182835778

image-20240327183020380

发现这里可以实现前面演示的攻击流程

image-20240327183320034

image-20240327184002061

有点看不懂,这里先去学一下pwntools的基本使用方法

附上笔记博客:pwntools的基本使用方法 – giraffe‘blog (giraffexiu.love)

image-20240327210643608

image-20240327211307594

攻击脚本中对齐rbp的栈顶,然后用0x90填充到0x70个字的大小

image-20240327212358572

先传入8个字的0x90覆盖rbp

再将buf的数据传入覆盖return adrsss的值,然后回到攻击脚本的开始的传入的asm指令

要理解这段指令

chorme搜索,查询execve

image-20240327215442835

image-20240327213950321

这里是说linux中 x64 的命令执行的格式模板

==这里的sys_execve表示在rax设置为59,rdi放入要执行的文件的名称,rsi 和rdx不想指定的话,可以赋值为0==

这样的话指令就会将程序指向攻击的脚本位置开始执行

经过动调后发现这里的bin/sh/文件在(buf+0x14)的位置

等待用户输入,先暂停运行进程,方便调试

<img src="https://g1rffe.oss-cn-chengdu.aliyuncs.com/typora/image-20240328163657238.png" alt="image-20240328163657238" style="zoom:80%;" />

或者

作用:

现在执行在read函数当中,然后finish的作用就是执行完这个read函数然后直接返回main函数

==省去传入参数后,在main函数下断点,然后再单步调试到这个函数结束然后跳转到main函数的过程==

基于前面的shellcode分析和ida的逆向分析

我们知道向rbp-0x70的位置传入了数据

image-20240328190505627

框住的部分应该就是shellcode,后面的部分是exp里面的填充到0x70的部分的0x90(nop)

<img src="https://g1rffe.oss-cn-chengdu.aliyuncs.com/typora/image-20240328224011799.png" alt="image-20240328224011799" style="zoom:200%;" />

这里应该是/bin/sh的值(猜测)

查一下

image-20240408212141217

image-20240415221900731

image-20240415221952606

image-20240415222537745

image-20240415222851597

rax返回了一个错误的值

image-20240415223200498

image-20240415223347309

image-20240416013238231

image-20240416013502885

简析部分指令:

endbr64 指令通常与 jmp(跳转)指令结合使用,以确保跳转目标的安全性。在执行 jmp 指令时,如果跳转目标地址附近存在 endbr64 指令插入的随机化标记,处理器会在跳转前检查该标记的存在性,以防止恶意利用分支预测攻击

用于启用基于分支目标地址的随机化(Branch Target Injection Mitigation)。这个指令主要是为了增强系统的安全性,特别是针对分支预测攻击,例如 Spectre 和 Meltdown。

image-20240416014020781

跳回到这的第一个entry,就会push(link_map)

image-20240416104716928

这里是在解析puts

image-20240416105046020

image-20240416105332795

image-20240416105359126

image-20240416105419468

image-20240416105901814

image-20240416105852710

image-20240416110141774

image-20240416110410423

image-20240416110626063

image-20240416120308671

image-20240416120357797

写入负数的话就会想上面的地址进行传入

image-20240416110859414

image-20240416111634736实际对应image-20240416111700012

rax对应value

array对应rcx

idx对应rdx

image-20240416111156809

image-20240416111909550

image-20240416115307672

image-20240416115326681

发现个array的地方的地址距离got表很近

可以利用前面发现的idx的漏洞进行劫持

image-20240416121916167

image-20240416122143366

image-20240416122359495

image-20240416122420838

image-20240416122502516

==(16进制)==

image-20240416123422921

通过获取 atoi 函数在 libc 中的地址,然后从 atoi 函数在内存中的地址中减去这个地址偏移量,得到的结果就是 libc 在内存中的基地址

image-20240416124040872

image-20240416124234063

这样我们的choice实际上就转换为

image-20240416124255959

就拿到shell了

方便调试的进行

image-20240416124846662

image-20240416125108446

image-20240416125234094

image-20240416125513100

image-20240416125619020

image-20240416125704136

image-20240416125746563

image-20240416125835379

image-20240416130905941

atoi就在这个区域

image-20240416130843712

根据这里的地址

image-20240416131201913

用基地址计算出这里的偏移

image-20240416131152355

反推出libc的地址

image-20240416131444434

发现在也在这个区域里面

image-20240416131509427

查看相对于libc的基地址的偏移

image-20240416131616583

将这里link出来的atoi的地址减去相对于libc的偏移0x47730,然后加上system的偏移0x55410就可以得到system的位置

利用泄露的atoi的地址,减去libc里面存储的偏移值

就可以得到libc基地址(就可以将他加上libc上面的存储的各种偏移,从而定位到其他的真实地址)

image-20240416132618844

image-20240416132914187

image-20240416133122774

跟进检查got表,这里printf,stevbuf等没有改,这里的atoi变成了system

image-20240416171211472

传入/bin/sh

image-20240416171432859

这里atoi接收/bin/sh

image-20240416171652530

带着这个参数,跳转到system

image-20240416171822830

comtinue就拿到shell了

image-20240416171921528

Gadget指可以利用的指令片段

只劫持一次流程,libc中有一些地址,跳进去就拿shell

工具最新版的下载地址

https://qiita.com/kusano_k/items/4a6f285cca613fcf9c9e#gl
ibc-231の場合

相关文章:

仮想関数テーブル、one-gadget RCE、glibc 2.31 #CTF - Qiita

跟前面的got hijack基本相同的源码(相同题目不同打法)

这里的基本思路是将这里atoi改写成gadget(这样的话无论buf传入什么都可以拿到shell)

image-20240417014045790

image-20240417014522862

image-20240417014849723

==其实这里可以不用atoi的got表,只要是一个可以呼叫到的一个entary就行,只要能执行就可以得到flag)

image-20240417014934294

image-20240417015243721

这里的execve就相当于这里多个寄存器配置好的一个shell

这里也可以直接==利用工具直接one gadget找到关键点==

==能利用的条件r12和r10为NULL或者指向的NULL==

image-20240417015709323

甚至可以对==binsh对应的地方进行交叉引用,来找到符合条件的one gadget==

image-20240417020111605

==模拟程序基本概况==

image-20240417020728050

image-20240417021009868

image-20240417020855277

返回的是gadget1的地址

image-20240417021152505

跳转后执行,将rdi的值push进去

image-20240417021251409

image-20240417021032329

==gadget1执行返回后,这里的再次返回地址改成gadget2的地址==

image-20240417021302163

执行gadget2

image-20240417021438925

image-20240417021622692

由此rsi=0

rdx=0

image-20240417021747391

rax=59

image-20240417021914995

这里最后将gadget3的值栈中,最后跳转到gadget3中执行

image-20240417022013216

==进入后执行syscall(系统调用)==

在 x86_64 架构上,使用 syscall 指令执行系统调用。执行 syscall 指令时,CPU 会执行以下操作:

image-20240417022227234

ssize_t read(int fd, void *buf, size_t count);
ssize_t read(int fd, void *buf, size_t count);
push rbp
mov rbp,rsp
sub rsp,20h
```
call function1
leave
ret
push rbp
mov rbp,rsp
sub rsp,20h
```
call function1
leave
ret
push rbp
mov rbp,rsp
sub rsp,30h
```
leave
ret
push rbp
mov rbp,rsp
sub rsp,30h
```
leave

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2024-7-18 19:18 被gir@ffe编辑 ,原因:
收藏
免费 2
支持
分享
最新回复 (1)
雪    币: 24068
活跃值: (62939)
能力值: (RANK:135 )
在线值:
发帖
回帖
粉丝
2
将标题补充一下,感谢分享!
2024-7-18 18:28
0
游客
登录 | 注册 方可回帖
返回
//