/Technology/Cybersecurity

[정보보호관리] 웹해킹 6문제 풀이

By Noah
||4
광고

Webhacking.kr 쿠키 변조 / XSS 문제 풀이 기록입니다. 이번 시리즈 주제: 쿠키 위변조, 필터 우회, XSS 인젝션


혹시 쿠키가 뭔지는 아시나요?

서버가 클라이언트에게 "이 사람은 로그인한 사람이야"라고 표시해주는 작은 데이터 조각입니다. 문제는, 그 데이터를 클라이언트가 직접 들고 다닌다는 겁니다.

즉, 개발자 도구를 열면 지금 이 순간 내 쿠키를 마음대로 수정할 수 있습니다.

서버가 쿠키를 신뢰한다면? 그게 바로 이번 문제들의 핵심입니다.


1. Webhacking.kr — old-01

🔗 https://webhacking.kr/challenge/web-01/ 유형: WEB | 핵심: 쿠키값 변조

"소수점 하나가 문을 열어줍니다"

문제에 접속하면 view-source 링크가 있습니다. 친절하게 소스코드를 보여주는 문제입니다. 바로 확인해봤습니다.

if(!is_numeric($_COOKIE['user_lv'])) $_COOKIE['user_lv']=1;
if($_COOKIE['user_lv']>=4) $_COOKIE['user_lv']=1;
if($_COOKIE['user_lv']>3) solve(1);

조건을 정리하면 이렇습니다.

  • 숫자가 아니면 → 1로 초기화

  • 4 이상이면 → 1로 초기화

  • 3 초과면 → solve

즉, user_lv3보다 크고 4보다 작은 값이어야 합니다. 정수로는 불가능합니다.

개발자 도구 → Application → Cookies에서 user_lv3.5로 바꿔줬습니다.

조건을 전부 통과하며 문제가 해결됩니다.

핵심 포인트

is_numeric()3.5, 3.1 같은 소수점도 숫자로 인식합니다. 정수 범위만 막고 소수점을 고려하지 않은 검증 로직의 허점입니다. 쿠키 값 검증은 타입과 범위를 동시에 처리해야 합니다.


2. Webhacking.kr — old-24

🔗 https://webhacking.kr/challenge/bonus-4/ 유형: WEB | 핵심: 쿠키 → 서버 변수 오염, str_replace 우회

"127.0.0.1을 직접 쓰면 막힙니다. 돌아가면 됩니다."

소스코드를 확인하니 핵심 로직이 두 줄로 요약됩니다.

extract($_SERVER);
extract($_COOKIE);  // 쿠키로 서버 변수를 덮어쓸 수 있음

extract($_COOKIE)extract($_SERVER) 이후에 호출됩니다. 즉, 쿠키에 REMOTE_ADDR 키를 넣으면 서버의 IP 주소를 덮어쓸 수 있습니다.

목표는 $ip == "127.0.0.1"을 만족시키는 것인데, 그냥 넣으면 아래 필터링에 걸립니다.

$ip = str_replace("12","",$ip);
$ip = str_replace("7.","",$ip);
$ip = str_replace("0.","",$ip);

127.0.0.1을 그대로 쓰면 12, 7., 0.이 전부 제거됩니다. 우회가 필요합니다.

풀이: 중첩 삽입으로 필터 우회

112277...00...00...1

str_replace는 한 번만 실행됩니다. 그래서 삭제될 부분을 안에 끼워넣으면 나머지가 합쳐져서 원하는 문자열이 됩니다.

  • 112212 제거 → 12

  • 77.7. 제거 → 7.

  • 00.0. 제거 → 0.

최종적으로 127.0.0.1이 완성됩니다. 이 값을 REMOTE_ADDR 쿠키에 넣고 요청하면 해결됩니다.

핵심 포인트

extract()는 배열의 키를 변수명으로 만들어주는 함수입니다. $_COOKIE를 아무런 필터 없이 extract()하면 공격자가 서버 내부 변수를 마음대로 오염시킬 수 있습니다. 실제 서비스에서 이 패턴은 사용하면 안 됩니다.


3. Webhacking.kr — old-32

🔗 https://webhacking.kr/challenge/code-5/ 유형: WEB | 핵심: 쿠키 기반 중복 방지 우회, 자동화

"100번 클릭하면 됩니다. 직접 하시겠습니까?"

리더보드에서 본인 닉네임을 클릭하면 투표 수가 1 올라갑니다. 100에 도달하면 문제가 해결됩니다.

단, 한 번 클릭하면 vote_check 쿠키가 생성되어 이후 투표가 막힙니다.

중복 방지 로직이 서버 세션이 아닌 클라이언트 쿠키로만 구현되어 있습니다. 쿠키를 안 보내면 그만입니다.

풀이 스크립트

import requests

URL = 'https://webhacking.kr/challenge/code-5/?hit=닉네임'
cookies = {'PHPSESSID': '세션ID'}

for i in range(100):
    res = requests.get(URL, cookies=cookies)
    print(f"[{i+1}/100] {res.status_code}")

vote_check 쿠키 없이 PHPSESSID만 담아서 100번 요청을 보냅니다. 스크립트를 돌리면 끝입니다.

핵심 포인트

중복 방지, 횟수 제한 같은 로직을 클라이언트 쿠키에만 의존하면 전혀 의미가 없습니다. 공격자는 쿠키를 삭제하거나 아예 안 보내면 그만이거든요. 상태 관리는 반드시 서버 사이드에서 해야 합니다.


4. Webhacking.kr — old-06

🔗 https://webhacking.kr/challenge/web-06/ 유형: WEB | 핵심: Base64 + 치환 인코딩 역산

"20번 인코딩을 풀면 됩니다. 손으로 하시겠습니까?"

소스코드를 보면 쿠키 값 생성 과정이 나옵니다.

  1. guest / 123qweBase64로 20번 인코딩

  2. 숫자 → 특수문자로 str_replace 치환

  3. 그 결과를 쿠키에 저장

서버는 쿠키를 받아서 반대로 특수문자 → 숫자로 치환 후, Base64를 20번 디코딩합니다. 최종 값이 admin / nimda이면 풀립니다.

역산하면 됩니다. adminnimda를 Base64로 20번 인코딩하고, 숫자를 특수문자로 치환한 값을 쿠키에 넣으면 됩니다.

풀이 스크립트

import base64

def replace_it(data):
    data = data.replace("1","!")
    data = data.replace("2","@")
    data = data.replace("3","$")
    data = data.replace("4","^")
    data = data.replace("5","&")
    data = data.replace("6","*")
    data = data.replace("7","(")
    data = data.replace("8",")")
    return data

ID = "admin"
PW = "nimda"

for i in range(20):
    ID = base64.b64encode(ID.encode("UTF-8")).decode("UTF-8")
    PW = base64.b64encode(PW.encode("UTF-8")).decode("UTF-8")

ID = replace_it(ID)
PW = replace_it(PW)

print("user 쿠키:", ID)
print("password 쿠키:", PW)

출력된 값을 user / password 쿠키에 넣고 새로고침하면 해결됩니다.

핵심 포인트

Base64는 암호화가 아닙니다. 인코딩입니다. 누구나 디코딩할 수 있고, 거꾸로 원하는 값을 인코딩해서 집어넣을 수도 있습니다. 쿠키 값을 Base64로 감싼다고 해서 보안이 생기는 게 아닙니다. 검증은 서버에서, 서명은 HMAC이나 JWT로 해야 합니다.


5. Google XSS Game — Level 1

🔗 https://xss-game.appspot.com/ 유형: WEB | 핵심: 기본 XSS

"가장 단순한 XSS입니다"

검색창에 입력값이 그대로 HTML에 반영됩니다. 필터링이 없습니다.

<script>alert(1)</script>

입력하면 alert가 뜨며 통과됩니다.

핵심 포인트

사용자 입력을 HTML에 그대로 출력하면 스크립트 태그가 실행됩니다. 모든 사용자 입력은 출력 전에 HTML 이스케이프(&lt;, &gt; 등) 처리가 필요합니다. 이게 XSS의 기본 중의 기본입니다.


6. Webhacking.kr — old-23

🔗 https://webhacking.kr/challenge/bonus-3/ 유형: WEB | 핵심: Null Byte Injection, eregi 우회

"영문자가 2개 붙으면 막힙니다. 사이에 끼워넣으면 됩니다."

code 파라미터로 값을 전달받아 화면에 출력하는 구조입니다. 목표는 <script>alert(1);</script> 를 실행시키는 것입니다.

문제는 영문자가 2개 이상 연속으로 나오면 필터링된다는 점입니다. script, alert 같은 단어가 전부 막힙니다.

eregi() 함수 기반 필터링인데, 이 함수는 Null Byte(%00)를 만나면 문자열 비교를 그 자리에서 멈춥니다. 영문자 사이마다 %00을 끼워넣으면 필터를 통과하면서 브라우저는 정상적으로 렌더링합니다.

풀이 페이로드

?code=<s%00c%00r%00i%00p%00t>a%00l%00e%00r%00t(1);</%00s%00c%00r%00i%00p%00t>

인접한 모든 영문자 사이에 %00을 삽입했습니다. eregi%00 앞까지만 비교하므로 필터를 통과하고, 브라우저는 Null Byte를 무시하고 스크립트를 실행합니다.

핵심 포인트

eregi()는 PHP 5.3에서 deprecated되어 현재는 사용이 권장되지 않습니다. Null Byte에 취약하다는 것도 그 이유 중 하나입니다. 필터링은 블랙리스트 방식보다 화이트리스트 방식이 훨씬 안전합니다. 허용할 것만 명시하는 것이 "막을 것을 전부 나열하는" 것보다 훨씬 견고합니다.


2차 회고

이번 시리즈를 통해 확실히 느낀 게 있습니다.

쿠키는 클라이언트가 들고 다닌다는 사실 자체가 취약점의 출발점입니다.

서버가 쿠키를 검증 없이 신뢰하는 순간, 공격자는 그 쿠키를 원하는 값으로 바꿀 수 있습니다. user_lv=3.5, REMOTE_ADDR=127.0.0.1, vote_check 없는 요청 — 전부 서버가 클라이언트를 신뢰했기 때문에 가능한 공격입니다.

그리고 XSS도 같은 맥락입니다. 사용자 입력을 신뢰하고 그대로 출력하면 안 됩니다.

"클라이언트에서 오는 모든 것은 조작될 수 있다"

이게 웹 보안의 제1원칙이라고 생각합니다.

다음 차시도 열심히 풀어보겠습니다. 🙂

저자 소개

DevOps Engineer. 금융과 여행에 관심이 많습니다.

독자 의견

Comments (0)