본문 바로가기

Webhacking.kr

old-52

처음 문제 화면을 열면 이런 페이지가 나타난다.

'admin page' 를 클릭하면 로그인 창이 뜬다.

‘취소’를 클릭하고 소스를 보자.

 

코드분석

<?php
include "config.php";
if($_GET['view_source']) view_source();
if($_GET['logout'] == 1){
  $_SESSION['login']="";
  exit("<script>location.href='./';</script>");
}
if($_SESSION['login']){
  echo "hi {$_SESSION['login']}<br>";
  if($_SESSION['login'] == "admin"){
    if(preg_match("/^172\\.17\\.0\\./",$_SERVER['REMOTE_ADDR'])) echo $flag;
    else echo "Only access from virtual IP address";
  }
  else echo "You are not admin";
  echo "<br><a href=./?logout=1>[logout]</a>";
  exit;
}
if(!$_SESSION['login']){
  if(preg_match("/logout=1/",$_SERVER['HTTP_REFERER'])){
    header('WWW-Authenticate: Basic realm="Protected Area"');
    header('HTTP/1.0 401 Unauthorized');
  }
  if($_SERVER['PHP_AUTH_USER']){
    $id = $_SERVER['PHP_AUTH_USER'];
    $pw = $_SERVER['PHP_AUTH_PW'];
    $pw = md5($pw);
    $db = dbconnect();
    $query = "select id from member where id='{$id}' and pw='{$pw}'";
    $result = mysqli_fetch_array(mysqli_query($db,$query));
    if($result['id']){
      $_SESSION['login'] = $result['id'];
      exit("<script>location.href='./';</script>");
    }
  }
  if(!$_SESSION['login']){
    header('WWW-Authenticate: Basic realm="Protected Area"');
    header('HTTP/1.0 401 Unauthorized');
    echo "Login Fail";
  }
}
?><hr><a href=./?view_source=1>view-source</a>

참고로 이 소스 코드는 admin 페이지의 소스 코드다.

취약점을 두 부분으로 나누어 보자.

1. HTTP Request Injection

if($_SESSION['login']){
  echo "hi {$_SESSION['login']}<br>";
  if($_SESSION['login'] == "admin"){
    if(preg_match("/^172\\.17\\.0\\./",$_SERVER['REMOTE_ADDR'])) echo $flag;
    else echo "Only access from virtual IP address";
  }
  else echo "You are not admin";
  echo "<br><a href=./?logout=1>[logout]</a>";
  exit;
}

admin으로 로그인하면 세션(Session)이라는 것이 생길 것이다.

반드시 admin으로 로그인이 되는 상태에서 공격이 가능하다.

 

또한, if(preg_match("/^172\\.17\\.0\\./",$_SERVER['REMOTE_ADDR']))코드를 보면 내부 ip에서만 접속 가능하다.

한마디로 로컬 환경에서만 공격이 된다는 얘기다.

2. SQL Injection

if($_SERVER['PHP_AUTH_USER']){
  $id = $_SERVER['PHP_AUTH_USER'];
  $pw = $_SERVER['PHP_AUTH_PW'];
  $pw = md5($pw);
  $db = dbconnect();
  $query = "select id from member where id='{$id}' and pw='{$pw}'";
  $result = mysqli_fetch_array(mysqli_query($db,$query));
  if($result['id']){
    $_SESSION['login'] = $result['id'];
    exit("<script>location.href='./';</script>");
  }
}

select id from member where id='{$id}' and pw='{$pw}'코드를 보자.

이 부분을 이용해 SQLi 부분은 매우 쉽게 공략 가능하다.

{$id} 부분에 admin’#를 넣으면 바로 SQLi가 된다.

select id from member where id='admin'# and pw='{$pw}'"

그래서 일단 SQLi로 admin계정으로 로그인을 해보자.

 

일단 admin 계정으로 로그인은 가능하지만, 로컬 ip로 한 것이 아니라서 FLAG 값을 얻을 수는 없다.

일단 문제의 인덱스(홈)페이지로 돌아가보자.

proxy 페이지가 보인다! 한번 들어가 보자.

이 시점에, 개발자 도구를 켜보면PHPSESSID값이 9qgs4tm207m0l7n8ajosafco3d 로 나온다.

그럼 이 값을 이용해서 HTTP Request Injection을 하면 된다.

 

익스플로잇

HTTP Request Injection

이 부분의 코드를 유심이 봐야한다.

if($_SESSION['login']){
  echo "hi {$_SESSION['login']}<br>";
  if($_SESSION['login'] == "admin"){
    if(preg_match("/^172\\.17\\.0\\./",$_SERVER['REMOTE_ADDR'])) echo $flag;
    else echo "Only access from virtual IP address";
  }
  else echo "You are not admin";
  echo "<br><a href=./?logout=1>[logout]</a>";
  exit;
}
  1. $_SESSION['login'] == "admin": 현재 로그인 세션이 admin이여야 한다. 즉, admin으로 로그인한 상태여야 한다.
  2. preg_match("/^172\\.17\\.0\\./",$_SERVER['REMOTE_ADDR']: 워게임 문제의 웹 서버가 돌아가는 로컬 서버에서만 공격 가능

이 두 가지를 공략해보자.

다시 proxy 페이지를 보면

프록시 페이지는 /페이지에 요청했을 때의 응답을 보여주고 있다.

우리의 소스코드는 admin페이지의 소스코드이므로, 우리는 admin페이지를 향해 요청을 보낸 것을 proxy페이지에서 확인해야 한다.

페이로드 작성

<http://webhacking.kr:10008/proxy.php?page=/admin/%20HTTP/1.1>

우선 admin페이지에 요청을 보내야 하고, HTTP의 버전은 맞춰야 하므로 다음과 같이 작성한다.

그리고 로그인 Session이 admin이여야 한다는 부분에 주목해 보면, HTTP 요청에 admin으로 로그인했을 때 sessionid(PHPSESSID)를 포함시켜야 한다는 것을 생각해낼 수 있다.

admin으로 로그인 했을 때 PHPSESSID값은 un0rfl56gvo839uueghdm3utis이다.

%0d%0a는 HTTP요청에서 개행을 의미한다.

%0d%0a: URL 인코딩된 CRLF (Carriage Return + Line Feed) 문자열

  • %0d = Carriage Return (\\r)
  • %0a = Line Feed (\\n)

즉, %0d%0a = \\r\\n HTTP 요청에서 줄바꿈(line break) 으로 쓰이는 표준이다.

%0d%0로 개행 표시를 하고 PHPSESSID를 포함해 HTTP요청을 전송해보면 다음과 같이 응답이 가는 것이다.

http://webhacking.kr:10008/proxy.php?page=/admin/%20HTTP/1.1%0d%0aCookie:PHPSESSID=un0rfl56gvo839uueghdm3utis

그런데 저렇게 보내면 Login Fail이라고 뜬다.

이것은 두 번째 줄에서 HTTP/1.1 에 대한 처리가 안 되서 그렇다.

HTTP 요청은 아래처럼 생겼다.

GET /admin/ HTTP/1.1\\r\\n
Header1: value1\\r\\n
Header2: value2\\r\\n
\\r\\n
[Optional Body]

여기서 헤더의 끝은 반드시 \\r\\n\\r\\n(즉, 빈 줄)로 끝나야 하고, 모든 헤더는 Key: Value 형식을 가져야한다.

그런데 페이로드에 /admin/%20HTTP/1.1%0d%0aCookie:PHPSESSID=un0rfl56gvo839uueghdm3utis이것을 추가하면, Cookie:줄이 무효화 되어 버린다.

그래서 PHPSESSID값을 넣은 후에 개행을 해준 뒤, User-Agent:를 붙이는 것이다.

 

User-Agent:를 붙여야 하는 다른 이유도 있다.

브라우저에서 직접 입력할 경우, %0d%0a를 몇 개 이상 연속으로 넣으면 브라우저나 프록시에서 필터링하거나 에러를 낼 수 있다.

예: %0d%0a%0d%0a → 일부 필터링에 걸려 작동 안 됨

그래서 정상적인 헤더처럼 보이게 하기 위해서 User-Agent: 라는 유효한 헤더를 하나 더 붙여준다.

최종 페이로드는 다음과 같다.

FLAG 값 : FLAG{Server_Side_Request_Forgery_with_proxy!}

 

핵심 요약

요소 설명

proxy.php 내부 요청을 대신 수행함 (curl 등)
%0d%0a 줄바꿈으로 강제 헤더 삽입
PHPSESSID=... 관리자 세션을 강제로 지정
내부 IP 조건 proxy 내부에서 요청이 발생해서 만족함

 

'Webhacking.kr' 카테고리의 다른 글

old-61  (0) 2026.05.14
old-27  (0) 2026.05.14
old-21  (0) 2026.05.14
old-32  (0) 2026.05.13
old-25  (0) 2026.05.13