jff 2회 대회 참가 후기및 웹문제들 write up

잡지식 2012.07.29 11:49



뭐 재미삼아 하는 대회라 참가했는데 평소 눈팅으로 네임드 분들이 많이 있어서 신선했다.


웹쪽은 3문제 밖에 출제되지 않았고 


소스를 공개하는 방식으로 보여줘서 삽질을 하지 않도록 배려한 주최측이 고마웠다.


점수 배점 선정방식이 그냥 주사위 두번굴려서


눈 나온거 곱하기 * 100 형식이었고


그덕분에..내가푼 웹 두문제는 둘다 400점으로 배점 받았지만


그래도 나름 즐기며 풀었다.



일단 내가 푼건 mathchall, nobaby 이 두문제.


소스코드는 둘다 첨부하였다.



mathchall.zip


nobaby.tgz



(tgz는 리눅스 알집형식. 윈도우 유져면 그냥 7zip 깔아놓으면 다풀린다.)



http://jff.b10s.org/ 


여기들어가면 아직도 풀 수 있는것같다. 다만 인증이 안될뿐


풀이 



1) mathchall


들어가보면 



이렇게 나온다.


Login을 눌르고 

join을 눌르면 알아서 아이디/비밀번호를 생성해주며 이걸로 로긴하고 probs로 가면 문제를푼다.


첨부된 소스코드를 보면


auth.php일부 --


 @$p = $_GET['p'];

 @$k = $_GET['k'];


 $row = mysql_fetch_array(mysql_query("select 1 from prob_ok where username = '".$_SESSION['math_login']."' and probname='$p'"));

 if(isset($row[0])) die("already clear!!");

 $row = mysql_fetch_array(mysql_query("select point from prob where auth = '$k' and probname='$p'"));

 if(isset($row[0])){

mysql_query("update user set point = point + ".$row['point']." where username='".$_SESSION['math_login']."'");

  echo("clear!!");

  mysql_query("insert into prob_ok values (null,'".$_SESSION['math_login']."','$p','".time()."')");

 }else{

  echo("wrong..");



(db.php참조 컬럼명,테이블명 다나옴)


여기서보면 prob_ok는 이미푼 문제들이다.

이미 푼 문제들이 아닌경우에 

mysql_query("update user set point = point + ".$row['point']." where username='".$_SESSION['math_login']."'");

점수를 update하고

echo를 뿌리고

  mysql_query("insert into prob_ok values (null,'".$_SESSION['math_login']."','$p','".time()."')");

이제 이문제는 풀었으니깐 prob_ok에다 집어넣는다.



처음에 이문제는 

레이스 컨디션 문제인것 같았다. (사실 지금도 레이스 컨디션 방식으로도 풀릴것같기도하다) 근데 아파치로 서버를 짯는지

자꾸 서버과부하가 걸려서 너무 느리고 안되더라. 


이문제는 일전에 비슷한 문제를 보았다.

secuinside2012 sqlgeek 문제

http://websec.wordpress.com/category/ctf/

참조


레이스컨디션 개요는 대략 이렇다

1) 첫 문제의 답이 담긴 쿼리를 뿌리고, 바로동시에 2)똑같은 쿼리를 하나더 뿌린다.


그러면 3) 첫번째쿼리문이 맞아서 update가 되고 echo를 뿌리고 insert 가 일어나기 전에 4) 두번째쿼리문이 한번더 update 시킨다.

5)그리고 나서 insert가 되면 결과적으로 문제의 점수가 2번 올라가게 된다. 

(첫 쿼리문의 insert가 두번째 쿼리문이 뿌려진후에 되는게 포인트)


그런데 이게 잘안되더라. 주최측에서는 계속 서버 껏다키기만하고 (초반에 문제를 많이 공개를 안해서 사람들이 이문제에 달려들어서 그런것같음)



그래서 한숨자고 다시 일어나서 풀었다.


계속 저 레이스컨디션이라는게 머리속에 맴돌아서 안풀리다가 

http://websec.wordpress.com/category/ctf/ 

다시 들어가봤는데

밑에 비슷한 레이스컨디션 문제를

https://rdot.org/forum/showthread.php?t=1330

이렇게 HEAD method의 버그로도 풀더라


Head method 버그가 뭔가하면, 저렇게 head로 request를 뿌리면

첫 output이 나오기전에 그냥 php가 작동을 멈춘다.


문제에 적용을하면

mysql_query("update user set point = point + ".$row['point']." where username='".$_SESSION['math_login']."'");

  echo("clear!!");

  mysql_query("insert into prob_ok values (null,'".$_SESSION['math_login']."','$p','".time()."')");


echo가 뜨기전에 멈추는것이다!!

그래서 그냥 여러번 값을 넣어주고


flag 취득조건인

$row = mysql_fetch_array(mysql_query("select point from user where username='".$_SESSION['math_login']."'"));


if(@$row[0] > 800){

mysql_query("delete from user where username='".$_SESSION['math_login']."'");

mysql_query("delete from prob_ok where username='".$_SESSION['math_login']."'");

session_destroy();

// get the flag!!

$flag = file_get_contents("../../flag.txt");

echo "<h1>".$flag."</h1>";


800점이상을 띄워주면 

정답이 나오더라.





2) nobaby


노 베이비는 

취약점이 member.php하나에 잇더라



$P = $_POST;

$m = new Mongo();

$db = $m -> selectDB("nobaby");

$c = $db -> users; 



switch($P['cmd']){

case "login_without_id":{

$_SESSION['level'] == 'guest';

$_SESSION['user'] = array('user_name' => $P['user_name'], 'user_security' => $P['user_s1']."-".$P['user_s2']);

ok("welcome guest","./main.php");

}break;


case "login_with_id":{

if($P['user_id']=="admin"){

if($_SERVER['REMOTE_ADDR']!="127.0.0.1"){

ok("admin is allowed only localhost");

}

}

$user = $c -> findOne(array('user_id' => $P['user_id'], 'user_ps' => $P['user_ps']));


if(!isset($user['user_id']))

ok("login failed..","./index.html");

if($user['user_id']=="admin")

$_SESSION['level'] = 'admin';

else

$_SESSION['level'] = 'user';

$_SESSION['user'] = $user;

ok("login ok","./main.php");

}break;


이부분보면 
로그인하는 부분인데
POST로 user_id와 user_ps를 받는다
user_id가 admin이면 필터링한다.
(admin is allowed only local host)


그후 mongodb object를 생성해서 

입력받은 user_id와 user_ps를 가지고 db에서 값을 뽑는다 (findOne orderb by 로 정렬한뒤 맨위에값 하나만 뽑음)


(mongodb는 query를 뿌릴때 find, findone 이런 함수를 이용하고 

이함수들이 string을 인자로 받지않고 array를 인자로 받는다는데 포인트가있다.

그리고 내부적으로 array key,value들은 다 string으로 인식된다. )



여기서 이게 admin이면

세션을 어드민으로 할당하고 

그담에 main.php에서 답을준다.


if(!isset($_SESSION['level'])) die("need login");

if($_SESSION['level'] == "admin"){

$flag=file_get_contents("../../nobaby_flag.txt");

die($flag);

}



몽고db를 구글해보니

http://www.goitworld.com/mongodb-is-vulnerable-to-sql-injection-in-php-at-least/

이렇게 나온다


mongodb로 구성해놓으면 쿼리를 


$collection->find(array( 
     "username" => "admin", 
     "passwd" => array("$ne" => 1) 
));

요렇게 받는데


얘는 내부적으로


mysql_query("SELECT * FROM collection 
    WHERE username="admin", 
    AND passwd!=1

요렇다. 


그러니깐 실제로 답을 넘겨 줄때 

(

$admin = array(

'user_id' => 'admin',

'user_ps' => md5('blueh4g'),

'user_name' => 'jaeyong kim',

'user_security' => "000000"."-".md5('1234567')

);

$c -> insert($admin,true);


admin정보)



cmd=login_with_id&user_id[$ne]=&user_ps=md5('blueh4g') 

이렇게 넘겨주면 

내부적으로

select * from db where id !='' and pw=답 order by ~~~ desc / asc 

이렇게 되니깐

user['user_id']했을때 admin이 나오고

결과적으로 문제가 풀린다.




여담이지만

처음에 

cmd=login_with_id&user_id[$ne]=qwer&user_ps=md5('blueh4g') 

이렇게 아이디 부분을 채워서 줬는데 로긴은 되는데 어드민껄로 안되었다.


아마 유저들이 풀때 회원가입을 md5('blueh4g')로 가입을 해서 정렬되다보니깐 admin이 맨위로 안나와서 그런듯..



아무튼 전체적으로 괜춘햇는듯

'잡지식' 카테고리의 다른 글

sql injection 필터우회의 모든것 -2  (2) 2012.08.04
webhacking.kr 44번 / 13번  (6) 2012.08.04
jff 2회 대회 참가 후기및 웹문제들 write up  (2) 2012.07.29
false injection  (0) 2012.07.17
근한달만이다.  (0) 2012.07.16
sql injection 필터우회의 모든것 -1  (0) 2012.07.14

설정

트랙백

댓글