Translated ['src/pentesting-web/hacking-with-cookies/README.md'] to ko

This commit is contained in:
Translator 2025-05-18 03:05:14 +00:00
parent 72a07cbc51
commit d7f3417ef0
2 changed files with 176 additions and 155 deletions

View File

@ -4,7 +4,7 @@
## 쿠키 속성
쿠키는 사용자의 브라우저에서 동작을 제어하는 여러 속성을 가지고 있습니다. 다음은 이러한 속성에 대한 설명입니다:
쿠키는 사용자의 브라우저에서 동작을 제어하는 여러 속성과 함께 제공됩니다. 다음은 이러한 속성에 대한 설명입니다:
### 만료 및 최대 연령
@ -20,7 +20,7 @@
### 정렬 규칙
두 개의 쿠키가 동일한 이름을 가질 때, 전송을 위해 선택되는 쿠키는 다음에 따라 결정됩니다:
두 개의 쿠키가 동일한 이름을 가질 때, 전송할 쿠키는 다음에 따라 선택됩니다:
- 요청된 URL에서 가장 긴 경로와 일치하는 쿠키.
- 경로가 동일할 경우 가장 최근에 설정된 쿠키.
@ -35,34 +35,34 @@
쿠키를 구성할 때 이러한 속성을 이해하면 다양한 시나리오에서 예상대로 동작하도록 보장할 수 있습니다.
| **요청 유형** | **예제 코드** | **쿠키 전송 시** |
| -------------- | -------------------------------- | ----------------- |
| 링크 | \<a href="...">\</a> | NotSet\*, Lax, None |
| -------------- | ------------------------------- | ----------------- |
| 링크 | \<a href="...">\</a> | NotSet\*, Lax, None |
| 프리렌더링 | \<link rel="prerender" href=".."/> | NotSet\*, Lax, None |
| 폼 GET | \<form method="GET" action="..."> | NotSet\*, Lax, None |
| 폼 POST | \<form method="POST" action="..."> | NotSet\*, None |
| iframe | \<iframe src="...">\</iframe> | NotSet\*, None |
| AJAX | $.get("...") | NotSet\*, None |
| 이미지 | \<img src="..."> | NetSet\*, None |
| iframe | \<iframe src="...">\</iframe> | NotSet\*, None |
| AJAX | $.get("...") | NotSet\*, None |
| 이미지 | \<img src="..."> | NetSet\*, None |
표는 [Invicti](https://www.netsparker.com/blog/web-security/same-site-cookie-attribute-prevent-cross-site-request-forgery/)에서 가져온 것이며 약간 수정되었습니다.\
표는 [Invicti](https://www.netsparker.com/blog/web-security/same-site-cookie-attribute-prevent-cross-site-request-forgery/)에서 가져왔으며 약간 수정되었습니다.\
_**SameSite**_ 속성이 있는 쿠키는 **CSRF 공격을 완화**합니다.
**\*Chrome80(2019년 2월)부터 쿠키에 SameSite 속성이 없는 경우 기본 동작은 lax**입니다 ([https://www.troyhunt.com/promiscuous-cookies-and-their-impending-death-via-the-samesite-policy/](https://www.troyhunt.com/promiscuous-cookies-and-their-impending-death-via-the-samesite-policy/)).\
이 변경을 적용한 후, Chrome에서 **SameSite 정책이 없는 쿠키는** **처음 2분 동안 None으로 처리되고, 이후에는 최상위 교차 사이트 POST 요청에 대해 Lax로 처리됩니다.**
이 변경을 적용한 후, Chrome에서 **SameSite 정책이 없는 쿠키는** **처음 2분 동안 None으로 처리되고, 이후 최상위 교차 사이트 POST 요청에 대해 Lax로 처리됩니다.**
## 쿠키 플래그
### HttpOnly
이 속성은 **클라이언트**가 쿠키에 접근하는 것을 방지합니다 (예: **Javascript**를 통해: `document.cookie`).
이 속성은 **클라이언트**가 쿠키에 접근하는 것을 방지합니다 (예: **Javascript**를 통해 `document.cookie`).
#### **우회 방법**
- 페이지가 요청의 응답으로 쿠키를 **전송하는 경우** (예: **PHPinfo** 페이지에서), XSS를 악용하여 이 페이지에 요청을 보내고 응답에서 **쿠키를 훔칠 수 있습니다** (예제는 [여기](https://hackcommander.github.io/posts/2022/11/12/bypass-httponly-via-php-info-page/)에서 확인하세요).
- **TRACE** **HTTP** 요청으로 우회할 수 있습니다. 서버의 응답은 전송된 쿠키를 반영합니다 (이 HTTP 메서드가 사용 가능한 경우). 이 기술은 **Cross-Site Tracking**이라고 합니다.
- 이 기술은 **모던 브라우저에서 JS로 TRACE 요청을 전송하는 것을 허용하지 않음으로써 방지됩니다**. 그러나 IE6.0 SP2와 같은 특정 소프트웨어에서 `TRACE` 대신 `\r\nTRACE`전송하여 우회하는 방법이 발견되었습니다.
- 페이지가 요청의 응답으로 쿠키를 **전송하는 경우** (예: **PHPinfo** 페이지에서), XSS를 악용하여 이 페이지에 요청을 보내고 응답에서 **쿠키를 훔칠 수** 있습니다 (예제는 [여기](https://blog.hackcommander.com/posts/2022/11/12/bypass-httponly-via-php-info-page/)에서 확인하세요).
- **TRACE** **HTTP** 요청으로 우회할 수 있으며, 서버의 응답은 전송된 쿠키를 반영합니다. 이 기술은 **Cross-Site Tracking**이라고 합니다.
- 이 기술은 **현대 브라우저에서 JS로 TRACE** 요청을 보내는 것을 허용하지 않음으로써 방지됩니다. 그러나 IE6.0 SP2에서 `TRACE` 대신 `\r\nTRACE`보내는 등의 특정 소프트웨어에서 우회 방법이 발견되었습니다.
- 또 다른 방법은 브라우저의 제로/데이 취약점을 악용하는 것입니다.
- 쿠키 항아리 오버플로우 공격을 수행하여 **HttpOnly 쿠키를 덮어쓸 수 있습니다**:
- 쿠키 항아리 오버플로우 공격을 수행하여 **HttpOnly 쿠키를 덮어쓸 수** 있습니다:
{{#ref}}
cookie-jar-overflow.md
@ -72,38 +72,38 @@ cookie-jar-overflow.md
### Secure
요청은 **보안 채널**(일반적으로 **HTTPS**)을 통해 전송되는 경우에만 HTTP 요청에서 쿠키를 **전송합니다**.
요청은 **HTTPS**와 같은 보안 채널을 통해 전송될 경우에만 HTTP 요청에서 쿠키를 **전송합니다**.
## 쿠키 접두사
`__Secure-`로 접두사가 붙은 쿠키는 HTTPS로 보호된 페이지와 함께 `secure` 플래그가 설정되어야 합니다.
`__Secure-`로 접두사가 붙은 쿠키는 HTTPS로 보호된 페이지에서 `secure` 플래그와 함께 설정되어야 합니다.
`__Host-`로 접두사가 붙은 쿠키는 여러 조건을 충족해야 합니다:
- `secure` 플래그 설정되어야 합니다.
- `secure` 플래그와 함께 설정되어야 합니다.
- HTTPS로 보호된 페이지에서 유래해야 합니다.
- 도메인을 지정할 수 없으며, 하위 도메인으로의 전송을 방지합니다.
- 도메인을 지정하는 것이 금지되어 하위 도메인으로의 전송을 방지합니다.
- 이러한 쿠키의 경로는 `/`로 설정되어야 합니다.
`__Host-`로 접두사가 붙은 쿠키는 슈퍼도메인이나 하위 도메인으로 전송될 수 없다는 점에 유의해야 합니다. 이 제한은 애플리케이션 쿠키를 격리하는 데 도움이 됩니다. 따라서 모든 애플리케이션 쿠키에 대해 `__Host-` 접두사를 사용하는 것은 보안 및 격리를 강화하는 좋은 관행으로 간주될 수 있습니다.
### 쿠키 덮어쓰기
따라서 `__Host-` 접두사가 붙은 쿠키의 보호 중 하나는 하위 도메인에서 덮어쓰는 것을 방지하는 것입니다. 예를 들어 [**쿠키 토스 공격**](cookie-tossing.md)을 방지합니다. [**쿠키 크럼블: 웹 세션 무결성 취약점 공개**](https://www.youtube.com/watch?v=F_wAzF4a7Xg) ([**논문**](https://www.usenix.org/system/files/usenixsecurity23-squarcina.pdf))에서 하위 도메인에서 __HOST- 접두사가 붙은 쿠키를 설정할 수 있는 방법이 제시되었습니다. 예를 들어, 시작 부분이나 끝 부분에 "="를 추가하여 파서를 속이는 방법입니다:
따라서 `__Host-` 접두사가 붙은 쿠키의 보호 중 하나는 하위 도메인에서 덮어쓰는 것을 방지하는 것입니다. 예를 들어 [**쿠키 토스 공격**](cookie-tossing.md)을 방지합니다. [**쿠키 크럼블: 웹 세션 무결성 취약점 공개**](https://www.youtube.com/watch?v=F_wAzF4a7Xg) ([**논문**](https://www.usenix.org/system/files/usenixsecurity23-squarcina.pdf))에서 하위 도메인에서 \_\_HOST- 접두사가 붙은 쿠키를 설정할 수 있는 방법이 제시되었습니다. 예를 들어, 시작이나 끝에 "="를 추가하여 파서를 속이는 방법입니다:
<figure><img src="../../images/image (6) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
한 PHP에서는 쿠키 이름의 시작 부분에 **다른 문자를 추가하여** **밑줄** 문자로 대체되도록 하여 `__HOST-` 쿠키를 덮어쓸 수 있었습니다:
는 PHP에서는 쿠키 이름의 시작 부분에 **다른 문자를 추가하여** 이를 **밑줄 문자**로 대체하여 `__HOST-` 쿠키를 덮어쓸 수 있었습니다:
<figure><img src="../../images/image (7) (1) (1) (1) (1).png" alt="" width="373"><figcaption></figcaption></figure>
## 쿠키 공격
사용자 정의 쿠키에 민감한 데이터가 포함되어 있는 경우 확인하세요 (특히 CTF를 진행 중인 경우), 취약할 수 있습니다.
사용자 정의 쿠키에 민감한 데이터가 포함되어 있다면 확인하세요 (특히 CTF를 진행 중이라면), 취약할 수 있습니다.
### 쿠키 디코딩 및 조작
쿠키에 포함된 민감한 데이터는 항상 면밀히 검토해야 합니다. Base64 또는 유사한 형식으로 인코딩된 쿠키는 종종 디코딩할 수 있습니다. 이 취약점은 공격자가 쿠키의 내용을 변경하고 수정된 데이터를 쿠키에 다시 인코딩하여 다른 사용자를 가장할 수 있게 합니다.
쿠키에 포함된 민감한 데이터는 항상 면밀히 검토해야 합니다. Base64 또는 유사한 형식으로 인코딩된 쿠키는 종종 디코딩할 수 있습니다. 이 취약점은 공격자가 쿠키의 내용을 변경하고 수정된 데이터를 다시 쿠키에 인코딩하여 다른 사용자를 가장할 수 있게 합니다.
### 세션 하이재킹
@ -111,7 +111,7 @@ cookie-jar-overflow.md
### 세션 고정
이 시나리오에서 공격자는 피해자 특정 쿠키를 사용하여 로그인하도록 속입니다. 애플리케이션이 로그인 시 새로운 쿠키를 할당하지 않으면, 원래 쿠키를 가진 공격자는 피해자를 가장할 수 있습니다. 이 기술은 피해자가 공격자가 제공한 쿠키로 로그인하는 데 의존합니다.
이 시나리오에서 공격자는 피해자 특정 쿠키를 사용하여 로그인하도록 속입니다. 애플리케이션이 로그인 시 새 쿠키를 할당하지 않으면, 원래 쿠키를 가진 공격자는 피해자를 가장할 수 있습니다. 이 기술은 피해자가 공격자가 제공한 쿠키로 로그인하는 데 의존합니다.
하위 도메인에서 **XSS를 발견했거나** 하위 도메인을 **제어하는 경우**, 읽어보세요:
@ -121,7 +121,7 @@ cookie-tossing.md
### 세션 기부
여기서 공격자는 피해자가 공격자의 세션 쿠키를 사용하도록 설득합니다. 피해자는 자신의 계정에 로그인했다고 믿고, 공격자의 계정 컨텍스트에서 무심코 작업을 수행하게 됩니다.
여기서 공격자는 피해자가 공격자의 세션 쿠키를 사용하도록 설득합니다. 피해자는 자신의 계정에 로그인했다고 믿고 공격자의 계정 컨텍스트에서 무심코 작업을 수행하게 됩니다.
하위 도메인에서 **XSS를 발견했거나** 하위 도메인을 **제어하는 경우**, 읽어보세요:
@ -133,7 +133,7 @@ cookie-tossing.md
이전 링크를 클릭하여 JWT의 가능한 결함을 설명하는 페이지에 접근하세요.
쿠키에서 사용되는 JSON 웹 토큰(JWT)도 취약점을 가질 수 있습니다. 잠재적인 결함과 이를 악용하는 방법에 대한 심층 정보를 얻으려면 JWT 해킹에 대한 링크된 문서를 참조하는 것이 좋습니다.
쿠키에서 사용되는 JSON Web Tokens (JWT)도 취약점을 가질 수 있습니다. 잠재적인 결함과 이를 악용하는 방법에 대한 심층 정보를 얻으려면 JWT 해킹에 대한 링크된 문서를 참조하는 것이 좋습니다.
### 교차 사이트 요청 위조 (CSRF)
@ -141,7 +141,7 @@ cookie-tossing.md
### 빈 쿠키
(자세한 내용은 [원본 연구](https://blog.ankursundara.com/cookie-bugs/)를 확인하세요) 브라우저는 이름 없이 쿠키를 생성하는 것을 허용하며, 이는 JavaScript를 통해 다음과 같이 시연할 수 있습니다:
(자세한 내용은 [원본 연구](https://blog.ankursundara.com/cookie-bugs/)를 참조하세요) 브라우저는 이름이 없는 쿠키 생성을 허용하며, 이는 JavaScript를 통해 다음과 같이 시연할 수 있습니다:
```js
document.cookie = "a=v1"
document.cookie = "=test value;" // Setting an empty named cookie
@ -167,38 +167,37 @@ document.cookie = "\ud800=meep"
#### 파싱 문제로 인한 쿠키 스머글링
(자세한 내용은 [원본 연구](https://blog.ankursundara.com/cookie-bugs/)를 참조하세요) Java(Jetty, TomCat, Undertow) 및 Python(Zope, cherrypy, web.py, aiohttp, bottle, webob)에서 제공하는 여러 웹 서버는 구식 RFC2965 지원으로 인해 쿠키 문자열을 잘못 처리합니다. 이들은 세미콜론이 포함되어 있어도 이중 인용된 쿠키 값을 단일 값으로 읽으며, 세미콜론은 일반적으로 키-값 쌍을 구분해야 합니다.
(자세한 내용은 [원본 연구](https://blog.ankursundara.com/cookie-bugs/)를 확인하세요) Java(Jetty, TomCat, Undertow) 및 Python(Zope, cherrypy, web.py, aiohttp, bottle, webob)에서 제공하는 여러 웹 서버는 구식 RFC2965 지원으로 인해 쿠키 문자열을 잘못 처리합니다. 이들은 세미콜론이 포함되어 있어도 더블 인용된 쿠키 값을 단일 값으로 읽으며, 이는 일반적으로 키-값 쌍을 구분해야 합니다:
```
RENDER_TEXT="hello world; JSESSIONID=13371337; ASDF=end";
```
#### 쿠키 주입 취약점
(자세한 내용은 [원본 연구](https://blog.ankursundara.com/cookie-bugs/)를 참조하세요) 서버가 쿠키를 잘못 파싱하는 경우, 특히 Undertow, Zope 및 Python의 `http.cookie.SimpleCookie``http.cookie.BaseCookie`를 사용하는 경우 쿠키 주입 공격의 기회를 제공합니다. 이러한 서버는 새로운 쿠키의 시작을 제대로 구분하지 못하여 공격자가 쿠키를 스푸핑할 수 있게 합니다:
(자세한 내용은 [원본 연구](https://blog.ankursundara.com/cookie-bugs/)를 참조하세요) 서버가 쿠키를 잘못 파싱하는 경우, 특히 Undertow, Zope 및 Python의 `http.cookie.SimpleCookie``http.cookie.BaseCookie`를 사용하는 경우 쿠키 주입 공격의 기회를 제공합니다. 이러한 서버는 새로운 쿠키의 시작을 적절히 구분하지 못하여 공격자가 쿠키를 스푸핑할 수 있게 합니다:
- Undertow는 세미콜론 없이 인용된 값 바로 뒤에 새로운 쿠키가 올 것으로 기대합니다.
- Zope는 다음 쿠키를 파싱하기 위해 쉼표를 찾습니다.
- Python의 쿠키 클래스는 공백 문자에서 파싱을 시작합니다.
이 취약점은 쿠키 기반 CSRF 보호에 의존하는 웹 애플리케이션에서 특히 위험합니다. 공격자는 스푸핑된 CSRF 토큰 쿠키를 주입하여 보안 조치를 우회할 수 있습니다. 이 문제는 Python의 중복 쿠키 이름 처리로 인해 악화되며, 마지막 발생이 이전 것을 덮어씁니다. 또한 불안전한 컨텍스트에서 `__Secure-``__Host-` 쿠키에 대한 우려를 불러일으키며, 쿠키가 스푸핑에 취약한 백엔드 서버로 전달될 때 권한 우회를 초래할 수 있습니다.
이 취약점은 쿠키 기반 CSRF 보호에 의존하는 웹 애플리케이션에서 특히 위험합니다. 공격자는 스푸핑된 CSRF 토큰 쿠키를 주입하여 보안 조치를 우회할 수 있습니다. 이 문제는 Python이 중복 쿠키 이름을 처리하는 방식으로 인해 악화되며, 마지막 발생이 이전 것을 덮어씁니다. 또한 불안전한 컨텍스트에서 `__Secure-``__Host-` 쿠키에 대한 우려를 불러일으키며, 쿠키가 스푸핑에 취약한 백엔드 서버로 전달될 때 권한 우회를 초래할 수 있습니다.
### 쿠키 $version
#### WAF 우회
[**이 블로그 포스트**](https://portswigger.net/research/bypassing-wafs-with-the-phantom-version-cookie)에 따르면, **`$Version=1`** 쿠키 속성을 사용하여 백엔드가 쿠키를 파싱하는 오래된 논리를 사용할 수 있을지도 모릅니다. 이는 **RFC2109** 때문입니다. 또한 **`$Domain`** 및 **`$Path`**와 같은 다른 값들도 쿠키와 함께 백엔드의 동작을 수정하는 데 사용될 수 있습니다.
[**이 블로그 포스트**](https://portswigger.net/research/bypassing-wafs-with-the-phantom-version-cookie)에 따르면, **`$Version=1`** 쿠키 속성을 사용하여 백엔드가 **RFC2109**로 인해 쿠키를 파싱하는 오래된 로직을 사용할 수 있을 가능성이 있습니다. 또한 **`$Domain`** 및 **`$Path`**와 같은 다른 값을 사용하여 쿠키와 함께 백엔드의 동작을 수정할 수 있습니다.
#### 쿠키 샌드위치 공격
[**이 블로그 포스트**](https://portswigger.net/research/stealing-httponly-cookies-with-the-cookie-sandwich-technique)에 따르면, 쿠키 샌드위치 기법을 사용하여 HttpOnly 쿠키를 훔칠 수 있습니다. 다음은 요구 사항 및 단계입니다:
- 응답에 명백히 쓸모없는 **쿠키가 반영되는 장소를 찾습니다**
- **값이 `1`인 `$Version`이라는 쿠키를 생성합니다** (JS에서 XSS 공격으로 이 작업을 수행할 수 있습니다) 더 구체적인 경로를 설정하여 초기 위치를 차지하게 합니다 (일부 프레임워크는 이 단계를 필요로 하지 않습니다)
- **반영되는 쿠키를 생성**하고 값에 **열린 큰따옴표**를 남기며, 이전 쿠키(`$Version`) 뒤에 위치하도록 특정 경로를 설정합니다
- 그러면 합법적인 쿠키가 순서상 다음에 오게 됩니다
- **큰따옴표를 닫는 더미 쿠키를 생성**합니다
- 응답에 명백히 쓸모없는 **쿠키가 반영되는 위치를 찾습니다.**
- **값이 `1`인 `$Version`이라는 쿠키를 생성합니다** (JS에서 XSS 공격을 통해 이 작업을 수행할 수 있습니다) 더 구체적인 경로를 설정하여 초기 위치를 차지하게 합니다 (Python과 같은 일부 프레임워크는 이 단계를 필요로 하지 않습니다).
- **반영되는 쿠키를 생성**하고 **열린 큰따옴표**를 남기는 값을 설정하며, 이전 쿠키(`$Version`) 뒤에 위치하도록 특정 경로를 설정합니다.
- 그러면 정당한 쿠키가 순서상 다음에 오게 됩니다.
- **큰따옴표를 닫는 더미 쿠키를 생성**하여 그 값 안에 포함시킵니다.
이렇게 하면 피해자 쿠키가 새로운 쿠키 버전 1 안에 갇히게 되고, 반영될 때마다 반영됩니다.
예: 포스트에서:
이렇게 하면 피해자의 쿠키가 새로운 쿠키 버전 1 안에 갇히게 되고, 반영될 때마다 반영됩니다.
```javascript
document.cookie = `$Version=1;`;
document.cookie = `param1="start`;
@ -220,7 +219,7 @@ document.cookie = `param2=end";`;
#### 쿠키 이름 차단 목록 우회
RFC2109에서는 **쿠키 값 사이에 쉼표를 구분자로 사용할 수 있다고 명시되어 있습니다**. 또한 **등호 앞뒤에 공백과 탭을 추가하는 것이 가능합니다**. 따라서 `$Version=1; foo=bar, abc = qux`와 같은 쿠키는 `"foo":"bar, admin = qux"`라는 쿠키를 생성하지 않고, `foo":"bar"``"admin":"qux"`라는 쿠키를 생성합니다. 두 개의 쿠키가 생성되는 방식과 admin의 등호 앞뒤 공백이 제거된 방식을 주목하세요.
RFC2109에서는 **쉼표가 쿠키 값 사이의 구분자로 사용될 수 있다고 명시되어 있습니다**. 또한 **등호 앞뒤에 공백과 탭을 추가하는 것이 가능합니다**. 따라서 `$Version=1; foo=bar, abc = qux`와 같은 쿠키는 `"foo":"bar, admin = qux"`라는 쿠키를 생성하지 않고, `foo":"bar"``"admin":"qux"`라는 쿠키를 생성합니다. 두 개의 쿠키가 생성되고 admin의 등호 앞뒤 공백이 제거된 것을 주목하세요.
#### 쿠키 분할을 통한 값 분석 우회
@ -231,26 +230,26 @@ Host: example.com
Cookie: param1=value1;
Cookie: param2=value2;
```
이 예제와 같이 WAF를 우회할 수 있습니다:
이 예제와 같이 WAF를 우회할 수 있게 해줄 수 있습니다:
```
Cookie: name=eval('test//
Cookie: comment')
Resulting cookie: name=eval('test//, comment') => allowed
```
### 추가 취약한 쿠키 검사
### Extra Vulnerable Cookies Checks
#### **기본 검사**
#### **Basic checks**
- **로그인**할 때마다 **쿠키**가 **같은**지 확인합니다.
- **쿠키**는 매번 **로그인**할 때 **같습니다**.
- 로그아웃하고 같은 쿠키를 사용해 보세요.
- 두 개의 장치(또는 브라우저)로 같은 계정에 같은 쿠키로 로그인해 보세요.
- 쿠키에 정보가 있는지 확인하고 수정해 보세요.
- 거의 같은 사용자 이름으로 여러 계정을 생성하고 유사성을 확인해 보세요.
- 거의 같은 사용자 이름으로 여러 계정을 생성해 보고 유사성을 확인하세요.
- "**기억하기**" 옵션이 존재하는지 확인하고 어떻게 작동하는지 살펴보세요. 존재하고 취약할 수 있다면, 다른 쿠키 없이 항상 **기억하기** 쿠키를 사용하세요.
- 비밀번호를 변경한 후에도 이전 쿠키가 작동하는지 확인하세요.
#### **고급 쿠키 공격**
#### **Advanced cookies attacks**
로그인할 때 쿠키가 동일하게 유지되거나 거의 동일하다면, 이는 쿠키가 귀하의 계정의 일부 필드(아마도 사용자 이름)와 관련이 있음을 의미합니다. 그러면 다음을 시도할 수 있습니다:
@ -258,7 +257,7 @@ Resulting cookie: name=eval('test//, comment') => allowed
- **사용자 이름을 브루트포스**해 보세요. 쿠키가 사용자 이름에 대한 인증 방법으로만 저장된다면, 사용자 이름 "**Bmin**"으로 계정을 생성하고 쿠키의 모든 **비트**를 **브루트포스**할 수 있습니다. 왜냐하면 시도할 쿠키 중 하나는 "**admin**"에 속하는 쿠키일 것이기 때문입니다.
- **패딩** **오라클**을 시도해 보세요(쿠키의 내용을 복호화할 수 있습니다). **패드버스터**를 사용하세요.
**패딩 오라클 - 패드버스터 예제**
**Padding Oracle - Padbuster examples**
```bash
padbuster <URL/path/when/successfully/login/with/cookie> <COOKIE> <PAD[8-16]>
# When cookies and regular Base64
@ -268,11 +267,11 @@ padbuster http://web.com/index.php u7bvLewln6PJPSAbMb5pFfnCHSEd6olf 8 -cookies a
padBuster http://web.com/home.jsp?UID=7B216A634951170FF851D6CC68FC9537858795A28ED4AAC6
7B216A634951170FF851D6CC68FC9537858795A28ED4AAC6 8 -encoding 2
```
Padbuster는 여러 번 시도하며 어떤 조건이 오류 조건(유효하지 않은 조건)인지 물어봅니다.
Padbuster는 여러 번 시도하며 어떤 조건이 오류 조건인지(유효하지 않은 조건) 물어봅니다.
그런 다음 쿠키를 해독하기 시작합니다(몇 분이 걸릴 수 있습니다).
공격이 성공적으로 수행되면, 원하는 문자열을 암호화해 볼 수 있습니다. 예를 들어, **encrypt** **user=administrator**를 원할 경우.
공격이 성공적으로 수행되었다면, 원하는 문자열을 암호화해 볼 수 있습니다. 예를 들어, **encrypt** **user=administrator**를 원한다면.
```
padbuster http://web.com/index.php 1dMjA5hfXh0jenxJQ0iW6QXKkzAGIWsiDAKV3UwJPT2lBP+zAD0D0w== 8 -cookies thecookie=1dMjA5hfXh0jenxJQ0iW6QXKkzAGIWsiDAKV3UwJPT2lBP+zAD0D0w== -plaintext user=administrator
```
@ -299,7 +298,7 @@ padbuster http://web.com/index.php 1dMjA5hfXh0jenxJQ0iW6QXKkzAGIWsiDAKV3UwJPT2lB
예를 들어 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"라는 사용자를 생성하고 쿠키에서 패턴이 있는지 확인합니다(ECB는 동일한 키로 모든 블록을 암호화하므로 사용자 이름이 암호화되면 동일한 암호화된 바이트가 나타날 수 있습니다).
사용된 블록의 크기로 패턴이 있어야 합니다. 따라서 "a"의 암호화된 묶음을 알고 있으면 사용자 이름을 "a"*(블록의 크기)+"admin"으로 만들 수 있습니다. 그런 다음 쿠키에서 "a" 블록의 암호화된 패턴을 삭제할 수 있습니다. 그러면 사용자 이름 "admin"의 쿠키를 얻게 됩니다.
사용된 블록의 크기로 패턴이 있어야 합니다. 따라서 "a"의 암호화된 묶음이 어떻게 되는지 알면 사용자 이름을 생성할 수 있습니다: "a"\*(블록 크기)+"admin". 그런 다음 쿠키에서 "a" 블록의 암호화된 패턴을 삭제할 수 있습니다. 그러면 사용자 이름 "admin"의 쿠키를 얻게 됩니다.
## References

View File

@ -1,27 +1,28 @@
/**
* HackTricks AI Chat Widget v1.15 Markdown rendering + sanitised
* ------------------------------------------------------------------------
* Replaces the static placeholder with a three-dot **bouncing** loader
* Renders assistant replies as Markdown while purging any unsafe HTML
* (XSS-safe via DOMPurify)
* ------------------------------------------------------------------------
* HackTricks AI Chat Widget v1.16 resizable sidebar
* ---------------------------------------------------
* Markdown rendering + sanitised (same as before)
* NEW: dragtoresize panel, width persists via localStorage
*/
(function () {
const LOG = "[HackTricks-AI]";
/* ---------------- User-tunable constants ---------------- */
const MAX_CONTEXT = 3000; // highlighted-text char limit
const MAX_QUESTION = 500; // question char limit
const LOG = "[HackTricksAI]";
/* ---------------- Usertunable constants ---------------- */
const MAX_CONTEXT = 3000; // highlightedtext char limit
const MAX_QUESTION = 500; // question char limit
const MIN_W = 250; // ← resize limits →
const MAX_W = 600;
const DEF_W = 350; // default width (if nothing saved)
const TOOLTIP_TEXT =
"💡 Highlight any text on the page,\nthen click to ask HackTricks AI about it";
"💡 Highlight any text on the page,\nthen click to ask HackTricks AI about it";
const API_BASE = "https://www.hacktricks.ai/api/assistants/threads";
const BRAND_RED = "#b31328"; // HackTricks brand
const BRAND_RED = "#b31328";
/* ------------------------------ State ------------------------------ */
let threadId = null;
let isRunning = false;
/* ---------- helpers ---------- */
const $ = (sel, ctx = document) => ctx.querySelector(sel);
if (document.getElementById("ht-ai-btn")) {
console.warn(`${LOG} Widget already injected.`);
@ -31,44 +32,37 @@
? document.addEventListener("DOMContentLoaded", init)
: init());
/* ==================================================================== */
/* 🔗 1. 3rd-party libs → Markdown & sanitiser */
/* ==================================================================== */
/* =================================================================== */
/* 🔗 1. 3rdparty libs → Markdown & sanitiser */
/* =================================================================== */
function loadScript(src) {
return new Promise((resolve, reject) => {
return new Promise((res, rej) => {
const s = document.createElement("script");
s.src = src;
s.onload = resolve;
s.onerror = () => reject(new Error(`Failed to load ${src}`));
s.onload = res;
s.onerror = () => rej(new Error(`Failed to load ${src}`));
document.head.appendChild(s);
});
}
async function ensureDeps() {
const deps = [];
if (typeof marked === "undefined") {
if (typeof marked === "undefined")
deps.push(loadScript("https://cdn.jsdelivr.net/npm/marked/marked.min.js"));
}
if (typeof DOMPurify === "undefined") {
if (typeof DOMPurify === "undefined")
deps.push(
loadScript(
"https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.2.5/purify.min.js"
)
);
}
if (deps.length) await Promise.all(deps);
}
const mdToSafeHTML = (md) =>
DOMPurify.sanitize(marked.parse(md, { mangle: false, headerIds: false }), {
USE_PROFILES: { html: true }
});
function mdToSafeHTML(md) {
// 1⃣ Markdown → raw HTML
const raw = marked.parse(md, { mangle: false, headerIds: false });
// 2⃣ Purify
return DOMPurify.sanitize(raw, { USE_PROFILES: { html: true } });
}
/* ==================================================================== */
/* =================================================================== */
async function init() {
/* ----- make sure marked & DOMPurify are ready before anything else */
try {
await ensureDeps();
} catch (e) {
@ -76,14 +70,14 @@
return;
}
console.log(`${LOG} Injecting widget… v1.15`);
console.log(`${LOG} Injecting widget… v1.16`);
await ensureThreadId();
injectStyles();
const btn = createFloatingButton();
createTooltip(btn);
const panel = createSidebar();
const panel = createSidebar(); // ← panel with resizer
const chatLog = $("#ht-ai-chat");
const sendBtn = $("#ht-ai-send");
const inputBox = $("#ht-ai-question");
@ -100,15 +94,8 @@
function addMsg(text, cls) {
const b = document.createElement("div");
b.className = `ht-msg ${cls}`;
// ✨ assistant replies rendered as Markdown + sanitised
if (cls === "ht-ai") {
b.innerHTML = mdToSafeHTML(text);
} else {
// user / context bubbles stay plain-text
b.textContent = text;
}
b[cls === "ht-ai" ? "innerHTML" : "textContent"] =
cls === "ht-ai" ? mdToSafeHTML(text) : text;
chatLog.appendChild(b);
chatLog.scrollTop = chatLog.scrollHeight;
return b;
@ -116,30 +103,28 @@
const LOADER_HTML =
'<span class="ht-loading"><span></span><span></span><span></span></span>';
function setInputDisabled(d) {
const setInputDisabled = (d) => {
inputBox.disabled = d;
sendBtn.disabled = d;
}
function clearThreadCookie() {
};
const clearThreadCookie = () => {
document.cookie = "threadId=; Path=/; Max-Age=0";
threadId = null;
}
function resetConversation() {
threadId = null;
};
const resetConversation = () => {
chatLog.innerHTML = "";
clearThreadCookie();
panel.classList.remove("open");
}
};
/* ------------------- Panel open / close ------------------- */
btn.addEventListener("click", () => {
if (!savedSelection) {
alert("Please highlight some text first to then ask HackTricks AI about it.");
alert("Please highlight some text first.");
return;
}
if (savedSelection.length > MAX_CONTEXT) {
alert(
`Highlighted text is too long (${savedSelection.length} chars). Max allowed: ${MAX_CONTEXT}.`
);
alert(`Highlighted text is too long. Max ${MAX_CONTEXT} chars.`);
return;
}
chatLog.innerHTML = "";
@ -157,11 +142,10 @@
addMsg("Please wait until the current operation completes.", "ht-ai");
return;
}
isRunning = true;
setInputDisabled(true);
const loadingBubble = addMsg("", "ht-ai");
loadingBubble.innerHTML = LOADER_HTML;
const loading = addMsg("", "ht-ai");
loading.innerHTML = LOADER_HTML;
const content = context
? `### Context:\n${context}\n\n### Question to answer:\n${question}`
@ -178,43 +162,39 @@
try {
const e = await res.json();
if (e.error) err = `Error: ${e.error}`;
else if (res.status === 429)
err = "Rate limit exceeded. Please try again later.";
else if (res.status === 429) err = "Rate limit exceeded.";
} catch (_) {}
loadingBubble.textContent = err;
loading.textContent = err;
return;
}
const data = await res.json();
loadingBubble.remove();
loading.remove();
if (Array.isArray(data.response))
data.response.forEach((p) => {
data.response.forEach((p) =>
addMsg(
p.type === "text" && p.text && p.text.value
? p.text.value
: JSON.stringify(p),
"ht-ai"
);
});
)
);
else if (typeof data.response === "string")
addMsg(data.response, "ht-ai");
else addMsg(JSON.stringify(data, null, 2), "ht-ai");
} catch (e) {
console.error("Error sending message:", e);
loadingBubble.textContent = "An unexpected error occurred.";
loading.textContent = "An unexpected error occurred.";
} finally {
isRunning = false;
setInputDisabled(false);
chatLog.scrollTop = chatLog.scrollHeight;
}
}
async function handleSend() {
const q = inputBox.value.trim();
if (!q) return;
if (q.length > MAX_QUESTION) {
alert(
`Your question is too long (${q.length} chars). Max allowed: ${MAX_QUESTION}.`
);
alert(`Question too long (${q.length}). Max ${MAX_QUESTION}.`);
return;
}
inputBox.value = "";
@ -228,9 +208,9 @@
handleSend();
}
});
}
} /* end init */
/* ==================================================================== */
/* =================================================================== */
async function ensureThreadId() {
const m = document.cookie.match(/threadId=([^;]+)/);
if (m && m[1]) {
@ -241,62 +221,67 @@
const r = await fetch(API_BASE, { method: "POST", credentials: "include" });
const d = await r.json();
if (!r.ok || !d.threadId) throw new Error(`${r.status} ${r.statusText}`);
threadId = d.threadId;
threadId = d.threadId;
document.cookie =
`threadId=${threadId}; Path=/; Secure; SameSite=Strict; Max-Age=7200`;
} catch (e) {
console.error("Error creating threadId:", e);
alert("Failed to initialise the conversation. Please refresh and try again.");
alert("Failed to initialise the conversation. Please refresh.");
throw e;
}
}
/* ==================================================================== */
/* =================================================================== */
function injectStyles() {
const css = `
#ht-ai-btn{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);width:60px;height:60px;border-radius:50%;background:#1e1e1e;color:#fff;font-size:28px;display:flex;align-items:center;justify-content:center;cursor:pointer;z-index:99999;box-shadow:0 2px 8px rgba(0,0,0,.4);transition:opacity .2s}
#ht-ai-btn:hover{opacity:.85}
@media(max-width:768px){#ht-ai-btn{display:none}}
#ht-ai-tooltip{position:fixed;padding:6px 8px;background:#111;color:#fff;border-radius:4px;font-size:13px;white-space:pre-wrap;pointer-events:none;opacity:0;transform:translate(-50%,-8px);transition:opacity .15s ease,transform .15s ease;z-index:100000}
#ht-ai-tooltip.show{opacity:1;transform:translate(-50%,-12px)}
#ht-ai-panel{position:fixed;top:0;right:0;height:100%;width:350px;max-width:90vw;background:#000;color:#fff;display:flex;flex-direction:column;transform:translateX(100%);transition:transform .3s ease;z-index:100000;font-family:system-ui,-apple-system,Segoe UI,Roboto,"Helvetica Neue",Arial,sans-serif}
#ht-ai-panel.open{transform:translateX(0)}
@media(max-width:768px){#ht-ai-panel{display:none}}
#ht-ai-header{display:flex;justify-content:space-between;align-items:center;padding:12px 16px;border-bottom:1px solid #333}
#ht-ai-header .ht-actions{display:flex;gap:8px;align-items:center}
#ht-ai-close,#ht-ai-reset{cursor:pointer;font-size:18px;background:none;border:none;color:#fff;padding:0}
#ht-ai-close:hover,#ht-ai-reset:hover{opacity:.7}
#ht-ai-chat{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:12px;font-size:14px}
.ht-msg{max-width:90%;line-height:1.4;padding:10px 12px;border-radius:8px;white-space:pre-wrap;word-wrap:break-word}
.ht-user{align-self:flex-end;background:${BRAND_RED}}
.ht-ai{align-self:flex-start;background:#222}
.ht-context{align-self:flex-start;background:#444;font-style:italic;font-size:13px}
#ht-ai-input{display:flex;gap:8px;padding:12px 16px;border-top:1px solid #333}
#ht-ai-question{flex:1;min-height:40px;max-height:120px;resize:vertical;padding:8px;border-radius:6px;border:none;font-size:14px}
#ht-ai-send{padding:0 18px;border:none;border-radius:6px;background:${BRAND_RED};color:#fff;font-size:14px;cursor:pointer}
#ht-ai-send:disabled{opacity:.5;cursor:not-allowed}
/* Loader animation */
.ht-loading{display:inline-flex;align-items:center;gap:4px}
.ht-loading span{width:6px;height:6px;border-radius:50%;background:#888;animation:ht-bounce 1.2s infinite ease-in-out}
.ht-loading span:nth-child(2){animation-delay:0.2s}
.ht-loading span:nth-child(3){animation-delay:0.4s}
@keyframes ht-bounce{0%,80%,100%{transform:scale(0);}40%{transform:scale(1);} }
::selection{background:#ffeb3b;color:#000}
::-moz-selection{background:#ffeb3b;color:#000}`;
#ht-ai-btn{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);min-width:60px;height:60px;border-radius:30px;background:linear-gradient(45deg, #b31328, #d42d3f, #2d5db4, #3470e4);background-size:300% 300%;animation:gradientShift 8s ease infinite;color:#fff;font-size:18px;display:flex;align-items:center;justify-content:center;cursor:pointer;z-index:99999;box-shadow:0 2px 8px rgba(0,0,0,.4);transition:opacity .2s;padding:0 20px}
#ht-ai-btn span{margin-left:8px;font-weight:bold}
@keyframes gradientShift{0%{background-position:0% 50%}50%{background-position:100% 50%}100%{background-position:0% 50%}}
#ht-ai-btn:hover{opacity:.85}
@media(max-width:768px){#ht-ai-btn{display:none}}
#ht-ai-tooltip{position:fixed;padding:6px 8px;background:#111;color:#fff;border-radius:4px;font-size:13px;white-space:pre-wrap;pointer-events:none;opacity:0;transform:translate(-50%,-8px);transition:opacity .15s ease,transform .15s ease;z-index:100000}
#ht-ai-tooltip.show{opacity:1;transform:translate(-50%,-12px)}
#ht-ai-panel{position:fixed;top:0;right:0;height:100%;max-width:90vw;background:#000;color:#fff;display:flex;flex-direction:column;transform:translateX(100%);transition:transform .3s ease;z-index:100000;font-family:system-ui,-apple-system,Segoe UI,Roboto,"Helvetica Neue",Arial,sans-serif}
#ht-ai-panel.open{transform:translateX(0)}
@media(max-width:768px){#ht-ai-panel{display:none}}
#ht-ai-header{display:flex;justify-content:space-between;align-items:center;padding:12px 16px;border-bottom:1px solid #333}
#ht-ai-header .ht-actions{display:flex;gap:8px;align-items:center}
#ht-ai-close,#ht-ai-reset{cursor:pointer;font-size:18px;background:none;border:none;color:#fff;padding:0}
#ht-ai-close:hover,#ht-ai-reset:hover{opacity:.7}
#ht-ai-chat{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:12px;font-size:14px}
.ht-msg{max-width:90%;line-height:1.4;padding:10px 12px;border-radius:8px;white-space:pre-wrap;word-wrap:break-word}
.ht-user{align-self:flex-end;background:${BRAND_RED}}
.ht-ai{align-self:flex-start;background:#222}
.ht-context{align-self:flex-start;background:#444;font-style:italic;font-size:13px}
#ht-ai-input{display:flex;gap:8px;padding:12px 16px;border-top:1px solid #333}
#ht-ai-question{flex:1;min-height:40px;max-height:120px;resize:vertical;padding:8px;border-radius:6px;border:none;font-size:14px}
#ht-ai-send{padding:0 18px;border:none;border-radius:6px;background:${BRAND_RED};color:#fff;font-size:14px;cursor:pointer}
#ht-ai-send:disabled{opacity:.5;cursor:not-allowed}
/* Loader */
.ht-loading{display:inline-flex;align-items:center;gap:4px}
.ht-loading span{width:6px;height:6px;border-radius:50%;background:#888;animation:ht-bounce 1.2s infinite ease-in-out}
.ht-loading span:nth-child(2){animation-delay:0.2s}
.ht-loading span:nth-child(3){animation-delay:0.4s}
@keyframes ht-bounce{0%,80%,100%{transform:scale(0);}40%{transform:scale(1);} }
::selection{background:#ffeb3b;color:#000}
::-moz-selection{background:#ffeb3b;color:#000}
/* NEW: resizer handle */
#ht-ai-resizer{position:absolute;left:0;top:0;width:6px;height:100%;cursor:ew-resize;background:transparent}
#ht-ai-resizer:hover{background:rgba(255,255,255,.05)}`;
const s = document.createElement("style");
s.id = "ht-ai-style";
s.textContent = css;
document.head.appendChild(s);
}
/* =================================================================== */
function createFloatingButton() {
const d = document.createElement("div");
d.id = "ht-ai-btn";
d.textContent = "🤖";
d.innerHTML = "🤖<span>HackTricksAI</span>";
document.body.appendChild(d);
return d;
}
function createTooltip(btn) {
const t = document.createElement("div");
t.id = "ht-ai-tooltip";
@ -311,11 +296,16 @@
btn.addEventListener("mouseleave", () => t.classList.remove("show"));
}
/* =================================================================== */
function createSidebar() {
const saved = parseInt(localStorage.getItem("htAiWidth") || DEF_W, 10);
const width = Math.min(Math.max(saved, MIN_W), MAX_W);
const p = document.createElement("div");
p.id = "ht-ai-panel";
p.style.width = width + "px"; // ← applied width
p.innerHTML = `
<div id="ht-ai-header"><strong>HackTricks AI Chat</strong>
<div id="ht-ai-header"><strong>HackTricks AI Chat</strong>
<div class="ht-actions">
<button id="ht-ai-reset" title="Reset"></button>
<span id="ht-ai-close" title="Close"></span>
@ -326,7 +316,39 @@
<textarea id="ht-ai-question" placeholder="Type your question…"></textarea>
<button id="ht-ai-send">Send</button>
</div>`;
/* NEW: resizer strip */
const resizer = document.createElement("div");
resizer.id = "ht-ai-resizer";
p.appendChild(resizer);
document.body.appendChild(p);
addResizeLogic(resizer, p);
return p;
}
/* ---------------- resize behaviour ---------------- */
function addResizeLogic(handle, panel) {
let startX, startW, dragging = false;
const onMove = (e) => {
if (!dragging) return;
const dx = startX - e.clientX; // dragging leftwards ⇒ +dx
let newW = startW + dx;
newW = Math.min(Math.max(newW, MIN_W), MAX_W);
panel.style.width = newW + "px";
};
const onUp = () => {
if (!dragging) return;
dragging = false;
localStorage.setItem("htAiWidth", parseInt(panel.style.width, 10));
document.removeEventListener("mousemove", onMove);
document.removeEventListener("mouseup", onUp);
};
handle.addEventListener("mousedown", (e) => {
dragging = true;
startX = e.clientX;
startW = parseInt(window.getComputedStyle(panel).width, 10);
document.addEventListener("mousemove", onMove);
document.addEventListener("mouseup", onUp);
});
}
})();