사이트에 들어가면 아래와 같은 화면이 나온다.
admin page를 클릭하면아래와 같은 페이지가 나온다.
전 페이지에서 나와있던 것처럼 guest/guest를 입력하고 로그인 해주면
admin이 아니고 guest라고 안내해주는 화면이 출력된다.
근데 내가 해야할 일은 admin 페이지에 접속하는 것이다.
로그아웃을 하고 로그인 창으로 돌아가 취소를 누르면 이런 화면이 나온다.
view-source를 누르면 아래와 같은 php 코드가 나온다.
<?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>
아직 PHP 코드에 익숙하지 않아서 좀 찾아보니,
$flag의 값을 얻기 위해서는 아래와 같은 조건들을 충족해야 한다.
1. $_SESSION['login'] 값이 "admin"이어야 함
2. $_SERVER['REMOTE_ADDR']가 172.17.0.x 대역이어야 함
1번 조건을 충족하기 위해서는 SQL Injection을 통해 공격해야 한다.
SQL 인젝션을 통해 비밀번호를 몰라도 admin에 로그인 할 수 있다고 하는데,
그 이유는 사용자로부터 입력된 값인 $_SERVER['PHP_AUTH_USER']와 $_SERVER['PHP_AUTH_PW'] 값들이 그대로 쿼리에 포함되어 있기 때문이라고 한다.
SQL Injection은 웹사이트의 보안상 허점을 이용해 특정 SQL 쿼리 문을 전송하여 공격자가 원하는 데이터베이스의 중요한 정보를 가져오는 해킹 기법을 말한다. 대부분 클라이언트가 입력한 데이터를 제대로 필터링하지 못하는 경우에 발생한다. 공격의 쉬운 난이도에 비해 피해가 상당하기 때문에 보안 위협 1순위로 불릴 만큼 중요한 기법이다.
그래서 [admin'-- ]을 아이디에 입력하고 비밀번호는 아무거나 입력해주면 admin page에 액세스 할 수 있다.
'--는 SQL에서 주석을 의미하는데, 주석 이후의 내용은 무시되므로, admin'--을 입력하면 쿼리가 다음과 같이 변형된다.
SELECT id FROM member WHERE id='admin'--' AND pw='[something]';
여기서 -- 이후의 내용은 모두 무시되므로, 쿼리는 사실상 다음과 같은 의미를 가진다.
SELECT id FROM member WHERE id='admin';
이렇게 되면 비밀번호 확인 절차가 무시되고, 데이터베이스는 id='admin'인 사용자를 반환하게 된다.
즉, 비밀번호 없이도 admin 계정으로 로그인할 수 있게 되는 이유이다.
admin만 입력했을 때는 로그인이 되지 않는데, 그 이유는 SQL 문이 완전하게 실행되기 때문이다.
주석(--)이 없으면 쿼리의 비밀번호 확인 절차가 포함되어 실행되므로, 올바른 비밀번호가 일치하지 않으면 로그인이 실패하게 된다.
SQL Injection을 통해 admin으로 로그인한 화면이다.
IP 주소가 해당 대역(172.17.0.x)이 아니라서 "Only access from virtual IP address"가 출력된 것을 확인할 수 있다.
그럼 이제 2번 조건 ($_SERVER['REMOTE_ADDR']가 172.17.0.x 대역이어야 함)을 충족하기 위해서는 프록시 header injection을 해야한다.
프록시 서버는 클라이언트가 자신을 통해서 다른 네트워크 서비스에 간접적으로 접속할 수 있게 해 주는 컴퓨터 시스템이나 응용 프로그램을 가리킨다.
서버와 클라이언트 사이에 중계기로서 대리로 통신을 수행하는 것을 가리켜 '프록시', 그 중계 기능을 하는 것을 프록시 서버라고 부른다.
HTTP 헤더 Injection
프록시 헤더 인젝션은 서버가 신뢰하는 클라이언트 IP를 조작하기 위해 사용됩니다. 사용자가 특정 IP 대역에서만 접근할 수 있도록 설정된 시스템에서는 클라이언트의 실제 IP를 기반으로 접근 권한을 부여한다.
하지만 이 IP를 프록시나 헤더를 통해 조작할 수 있다면, 공격자가 원하는 IP 대역에서 접근한 것처럼 속여 특정 조건을 우회할 수 있다.
헤더 인젝션 공격에서 쿠키값이 필요한 이유는 쿠키가 사용자의 로그인 세션을 인증하기 때문이다.
쿠키는 사용자가 이미 로그인한 상태임을 서버에 알리는 수단이므로, 공격자는 다음과 같은 과정을 통해 서버의 보호된 리소스에 접근할 수 있다.
1) 로그인 과정: 공격자가 먼저 서버에 정상적으로 로그인하여 세션 ID를 포함한 쿠키를 얻는다. 이 쿠키는 서버와 클라이언트 간의 모든 요청에서 사용자의 인증 정보를 담고 있다.
2) 쿠키 사용: 공격자는 로그인 후 얻은 쿠키값을 이용해 자신이 인증된 사용자라는 것을 서버에 알린다. 이때 쿠키가 없으면 서버는 사용자가 인증되지 않았다고 판단하고, 접근을 거부할 것이다.
3) 헤더 인젝션 적용: 공격자는 쿠키값을 포함한 요청에서 IP를 조작하여 서버가 허용된 가상 IP 대역에서 접속한 것처럼 보이게 만든다. 이때 프록시 헤더(X-Forwarded-For)에 172.17.0.x 범위의 IP를 주입한다.
=> 즉, 쿠키값은 인증을 위한 것이고, 헤더 인젝션은 서버의 IP 주소 검사를 우회하기 위한 것이다. 공격자가 로그인 세션을 유지하지 못하면 서버는 헤더 인젝션에 성공하더라도 플래그를 보여주지 않는다.
proxy 클릭하면 이런 화면이 나온다.
admin 페이지로 가서 쿠키값을 가져온다. (F12 -> Application -> cookies)
기존 프록시 페이지를 접속했을 때 url 주소이다.
아래와 같이 쿠키값을 포함하여 url을 바꾸어준다.
admin/%20HTTP/1.1%0d%0aCookie:%20PHPSESSID=etg3haii5po5ml4i4pfdj4gd6f%0d%0aUser-Agent:
CRLF 인젝션 (Carriage Return and Line Feed)
%0d와 %0a는 각각 Carriage Return (\r)과 Line Feed (\n)를 의미한다.
이는 새로운 줄을 의미하는 문자로, HTTP 프로토콜에서 중요한 역할을 한다. HTTP 요청은 헤더와 본문을 구분하는데, 각 헤더 필드는 CRLF를 사용하여 구분된다.
예를 들어, 다음과 같은 일반적인 HTTP 요청을 살펴보면
GET / HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0
각 줄의 끝에는 보이지 않는 CRLF(\r\n)가 있다. 이 줄들이 헤더의 각 필드를 구분하고, 두 개의 연속된 CRLF가 나오면 헤더가 끝나고 본문이 시작된다는 신호를 보낸다.
이 공격에서 사용된 %0d%0a는 CRLF 인젝션을 통해 URL에 헤더 필드를 추가하거나 조작할 수 있게 만든다.
이와 같이 URL을 조작하면 서버가 이를 처리할 때, GET 요청의 헤더에 삽입된 CRLF로 인해 다음과 같은 형식의 HTTP 요청이 만들어질 수 있다.
admin/%20HTTP/1.1%0d%0aCookie:%20PHPSESSID=etg3haii5po5ml4i4pfdj4gd6f%0d%0aUser-Agent:
GET /admin/ HTTP/1.1
Cookie: PHPSESSID=etg3haii5po5ml4i4pfdj4gd6f
User-Agent:
admin/%20HTTP/1.1%0d%0aCookie:%20PHPSESSID=etg3haii5po5ml4i4pfdj4gd6f%0d%0aUser-Agent:와 같은 URL을 통해 헤더 인젝션이 성공한 이유는 HTTP 요청의 헤더를 URL을 통해 조작할 수 있었기 때문이다. 이를 통해 서버는 URL에 포함된 데이터를 HTTP 헤더로 잘못 해석하게 되었고, 공격자가 원하는 대로 요청이 전달된 것입니다. 여기서 주요한 요소는 CRLF(Carriage Return and Line Feed) 인젝션과 HTTP 헤더의 직접 조작이다.
FLAG 획득!
참고
https://velog.io/@jjmoon4682/HTTP-%ED%97%A4%EB%8D%94-%ED%94%84%EB%A1%9D%EC%8B%9C]
'SWLUG > CTF' 카테고리의 다른 글
[CTF/ Dreamhack] blind sql injection advanced (0) | 2024.09.30 |
---|---|
[CTF/ Dreamhack] CProxy: Inject (0) | 2024.09.25 |
[WebHack][WebGoat] 환경세팅 & Hijack a session (1) | 2024.09.16 |
[CTF/ Dreamhack] Type c-j (0) | 2024.05.23 |
[CTF/ 써니나타스] Web -1 (0) | 2024.05.23 |