pwn学习笔记

PWN学习笔记——初稿

注明:本篇学习笔记是入门学习pwn时的随笔,所以不具有任何参考价值 ,仅作为对学习路线回顾参考的笔记以及入门纪念。

使用 Kali Linux , 称霸局域网 ![doge]


调试,调试!

Ida中的静态调试显示的函数和输出之间的距离,有时候会因为各种原因而显示错误,需要用pwndbg动态调试观察一下。淦,gdb显示的地址有时候也不对

IDA基本操作 (静态调试)

shift + F12

space

F5

ctrl + F

Pwndbg基本操作(动态调试)

Pwndbg + 文件名  进入对某个文件的调试

r 运行

b + 地址/函数  设置断点

checksec + 文件名  查看保护措施

stack + 数字  查看栈段

vmmap' 显示虚拟内存的的空间分布(观察读写的权 限是否有冲突-w与x不同时出现

plt 查看文件里的plt表项

x + 地址 显示该地址的内容和地址

got 查看got中保存的函数数量及其字节、数据等信息

disass + 地址 反汇编地址的数据,进而查看其汇编代码或plt

c 继续执行程序,直到遇到断点/程序中断

start 开始执行程序,会停到main函数;如果没有main函数,则会停到程 序的入口

s 附近

backtrace 显示函数调用栈段的状态

return 快速回到mian函数


求求了,用用py吧

(context.arch = “amd64”)  调整攻击环境 

shellcode.sh()

print(xxx)  更清楚地查看shellcode

print(asm(xxx))  查看shellcode的机器码

获取shellcode : 1.shellstore 2.pwntools – shellcraft.sh() (后面就是方法) 

Print(asm(shellcode.(amd64.)sh()))

 


嘿嘿嘿·,脚本(介绍一下大致模板,最简单的那种啊,别误会)

vim exp.py

from pwn import *

(context.arch = "amd64")

io = process("本地文件路径") //与本地文件交互

OR  

io = remote("IP", 端口 )

io.recvline() //接受程序给的文件

payload = b'”x”(字节型的数据) + p32(劫持程序的地址) //将一个整数的地址转化成字节型的数据 32位的 ebp占4个字节,64位的·ebp占8个字节
io.send(payload) //发送数据  

OR  

io.sendline(payload) //发送一行数据,相当于在数据末尾加/n;

io.interactive()   //交互
from pwn import *

(context.arch = "amd64")

io = process("本地文件路径") //与本地文件交互

OR  

io = remote("IP", 端口 )

io.recvline() //接受程序给的文件

asm(shellcode.(amd64.)sh()) //获取shellcode的机器码    

payload = asm( shellcode.(amd64.)sh()),lhust( 112, b’A ) /*补充字节流(这里就是A )到112 */ + p32( 劫持程序的地址(可以是全局变量的地址) )

io.send(payload) //发送数据  

OR  

io.sendline(payload) //发送一行数据,相当于在数据末尾加/n;

io.interactive()   //交互

还有很多呐!

 


呐,shellcode注意啥啊

bss默认可执行,因此我们可以通过全局变量向其输入shellcode

gets 漏洞

未初始化的全局变量保存在bss里

 


不会算数,py来救

使用python计算栈段之间的距离

0x….(高地址) – 0x…(低地址)

>>>十进制数

Hex(十进制)

>>>十六进制

 


保护措施

1-NX 栈不可执行

2-ASLR 让函数的栈、共享连接库、堆段进行地址随机化,

0 – 未打开;1 – 部分随机化;2 – 全部随机化

关闭ASLR – “echo 0 > /proc/sys/kernel/randomize_va_space”

3-Canary(金丝雀) 在调用一个函数,刚创建栈帧时,首先把一个值(canary)放在低地址空间里,在销毁 函数时,先检查这个值(canary)是否发生改变,如果值改变,就会其强行将程序退出

4-Pie 编译时打开开关,随机化elf文件映像(text,bss。data)

 


程序进不去怎么办

修改文件权限

#!/bin/sh

gcc -fno-stack-protector -z execstack -no-pie -g -o re2stack ret2stack.c

-fno-stack-protector 关闭canary,使栈溢出首先可行

-z execstack  打开栈的可执行权限

-no-pie  关闭pie

-g  带上调试信息,便于观看(一定要带着re2stack.c)

-o  输出文件名


请问字节是啥

8比特 – 1 字节

每4比特可以直接写成一个16进制的值,

每两个16进制数就是1个字节


ROP!!!

没有连续的代码,但是通过一段一段的这个函数中包含的代码片段,达到相同的执行效果,即模拟程序的执行

其中思想的不同:没有一步到位的地址一次覆盖到tet,调用shell(即调用shell攻击目标,无法通过一个地址一次,所以需要,自己将程序中的指令组合起来,起到shell的作用)

我们所需要的为eaxebx…赋值的代码本来就存在,但是不连续,我们需要把他们组成一个链式结构(gadget),所以需要溢出很长的一段数据,把这些调成想要的格式,使其能够连续工作,并使其最后一条指令为ret。

对于系统调用,要在所有以ret结尾的代码中,找出个代码片段,它们的作用分别是pop eax、pop ebx、pop ecx、pop edx,pop一个寄存器的指令可以将数值直接写到栈上

PS:系统调用 —— 本质上只是一段函数,X86使用中断进行系统调用,第一个栈帧是main函数的栈帧,在main函数之前执行的所有函数都是没有栈帧的


ret2syscall

因为需要执行大量代码段,故通过局部变量溢出, 组合一些gadget执行大量的代码片段,故将从ret开始的一大段栈空间,覆盖成gadget地址和对应的参数,此时,在main函数返回时,就会返回到已经被覆盖掉的,现在是ret的地址, 并执行指令

(ret把当前栈顶的值pop到eip)ret等效于pop eip

ROP

ROP1 

get ­­­­­­­­­­­­­­­­­­­­--binary 文件名 -- only “ pop | ret 要寻找的指令|管道符 grep xx 把输入参数中含有xx的命令显示出来

Ret = pop xxx 即把xxx弹出到eip 

Xor 清空寄存器

下面是示例!

ret2syscall02ret2syscall01

Int 0x80 ( eax = 0xb , ebx = …, ecx = 0,edx = 0 )进行系统调用( int – 中断指令 ),通过寄存器传参,来确定要调用哪一个函数。在执行int 0x80 这个汇编代码对应的机器码时,要确保四个寄存器都已经存储了对应的函数所需要的参数,即0xb这一个系统调用号对应的sys_execve() 对应的内核里的函数的调用号,而0xb 在这里·也带代表了那个函数

下面使用pwntools便捷获取/bin/sh的地址

pwntools

使用next传入生成器

pwntools1

最后一层是要输入的垃圾数据,倒数第三行是要给

倒数第二层输入的数据,以此类推

ret2ssystem

 


动态链接 

当我们使用file命令去查看一些文件的属性时,下面两点也会被显示出来:

动态链接:gcc -fno-pie -o dytest xxx.py   dynamically link

静态链接:gcc -fno-poe --static -o statest xxx.py   statically link

本块主要讨论动态链接

动态链接相关结构

动态链接结构

dynamic section 为操作系统描述了整个动态链接的完整内容

提供动态链接相关信息

link_map 保存进程载入的动态链接库的链表

dl_runtime_resolve 解析第一次在动态链接的函数的真实地址

装载器中用于解析动态链接库中函数的实际地址的函数

got  全局的符号、变量地址

got.plt 全局的函数地址

 

动态占用内存小(其库函数占用少)

 

动态链接过程

ldd 文件名 查看文件用到的所有动态链接库

动态链接

下面分流程分析

过程1

text —— 代码节;foo@olt —— foo是自己写的函数;plt是代码段中存放函数真实地址的一个节; PLT0(PLT最开始的两段指令)

因为foo是个动态链接库中的代码,所以call foo,并不能直接跳转到它自己代码段里的foo函数,只能去代码段中的plt节,而每个被调用的动态链接库中的函数都会在其中创立一个表项,

 

1、call foo@plt 进程首次调用 foo

2、jmp *(foo@GOT) 跳转到 .plt 中的 foo 表项,plt 中的代码立即跳转到.got.plt 中记录的地址

3、push 由于进程是第一次调用 foo,故.got.plt 中记录的地址是 foo@plt+1

4、jmp 回到 .plt 是,解析 foo 的实际地址

5、push *(GOT+4) 跳转到 .plt 头部,为 dl_runtime_resolve 函数传参

6、push *(GOT+4) 跳转到 .plt 头部,为 dl_runtime_resolve 函数传参

过程2

7、call_fix_up dl_runtime_resolve 函数解析 foo 的真正地址填入 .got.plt 中

8、ret 0xc 此后 .got.plt 中保存的是 foo 的真实地址

过程3

9、call foo@plt 系统第二次调用foo

10、jum *(foo@GOT)  直接自 .got.plt 跳转到 foo 的真实地址,没有了第一次的解析地址过程

ok,接下来展示完整流程

完整


IDA的细节

init 用作初始化的一个节,记录了初始化代码

plt 存放函数真实地址的一个节

got 存放数据的节


ret2libc

往往依赖于rop所需要的各种gadget创造执行shellcode的环境,但其目标是返回libc里的system函数这一类可以为我们提供一个shell的函数

 

原文地址:https://www.cnblogs.com/socialbiao/p/15669045.html