Metasploit API와 msfrpcd, 그리고 NodeJS

요즘 metasploit의 rpc에 대해 굉장히 관심이 많습니다. 대표적으론 msgrpc plugin 부터 msfrpcd 데몬까지 metasploit에서 rpc를 이용할 수 있는 방법들을 많이 찾아보고 있지요.

제가 주로 루비를 많이 쓰긴 하지만 취약점을 찾고 보안 테스팅을 하는 직업의 특성 상 Javascript는 익숙해질 수 밖에 없습니다. 잘 생각해보면 블로그 관리에도 JS와 CSS를 수시로 변경하기 떄문에 감을 쉽게 잃어버리지는 않는 것 같습니다.

오늘은 바로 서버 사이드의 Javascript인 NodeJS 와 msfrpcd를 이용해 Metasploit을 가지고 노는 이야기를 할까 합니다.

MsfRPC

msfrpc란 metasploit에서 제공하는 rpc 서버로 보시면 좋습니다. metasploit을 rpc서버처럼 사용하여 API로 정보 교환, 명령어 전달이 가능합니다. 이를 활용하면 msfconsole이 아니여도 다른 형태의 UI 구성을 이룰 수 있지요. 대표적으론 Armitage 또한 msfrpc를 이용해서 UI 와 Metasploit Framework를 연결하는 것으로 알고있습니다.

msfrpc의 동작은 Ruby Object 기반의 JSON 형태로 통신하기 때문에 어렵지 않습니다. JSON 포맷으로 데이터를 전송하면 동일하게 JSON 포맷으로 데이터를 받습니다.

{
  "version" => "4.0.0-release",
  "ruby" => "1.9.1 x86_64-linux 2010-01-10"
}

물론 직접 파싱해서 사용해도 되고 라이브러리를 이용해 쉽게 사용할수도 있습니다.

RPC in NodeJS

NodeJS 에서 msfrpc를 쉽게 다루기 위해선 라이브러리 설치가 필요합니다. 물론 프로토콜에 대한 이해가 있다면 라이브러리 없이 가능하겠지만, 그래도 선구자분들이 열심히 만들어놨기 때문에 잘 사용해서 쉽게 만들어갑시다.

먼저 npm을 통해 msfrpc-client-node 의 설치가 필요합니다.

npm install msfrpc-client-node --save

그러고나서 msfrpcd 를 열어준 후 코드를 작성합니다.

#> ./msfrpcd -U test -P 1234
[*] MSGRPC starting on 0.0.0.0:55553 (SSL):Msg...
[*] MSGRPC backgrounding at 2017-08-22 18:28:26 +0900...

먼저 MsfRpcClient 로 새로운 객체를 만들어주는데, 인자로 msfrpc로 넘길 정보를 같이 넘겨줍니다. 대표적으로 user / password 부터, host address, port 정보가 되겠지요. 그다음 exec 메소드를 통해 msfrpc 서버로 요청을 전달할 수 있습니다. 해당 메소드로 전송하게 될 떄 위에서 설명드린 것 같이 JSON 형태로 요청이 발송되고, 수신 또한 JSON으로 받게됩니다.

var MsfRpcClient = require('msfrpc-client-node');
var client = new MsfRpcClient({
                                password:'1234', // password 항목은 msfrpc의 password
                                user:'test', // user 항목은 msfrpc의 user
                                host:'127.0.0.1', // host 주소
port: '55553', // port 번호
                                persist:false
                              });

// client에 MsfRpcClient의 객체가 있기 때문에
// exec 메소드로 msfrpc 서버에 명령 전송이 가능합니다.

client.exec(['core.version']) // Metasploit의 버전을 받아오겠습니다.
.then(
  (res)=>{
    console.log(`MSF Version : ${res.version} `)
    console.log(`API Verson: ${res.api}`)
  }
)
.catch(console.log);

이후 MsfRpcClient를 이용해서 msfrpcd에 접속 후 exec로 명령을 날려줍니다. 간단하게 core.version을 가져와봅니다.

node msgrpc_node.js
MSF Version : 4.15.7-dev-98ba671
API Verson: 1.0

잘 되네요. 그럼 아래 구문을 추가해서 현재 module 들의 상태를 봐볼까요?

client.exec(['core.module_stats'])
.then(
  (res)=>{
    console.log(res);
  }
)
.catch(console.log);
node msgrpc_node.js
MSF Version : 4.15.7-dev-98ba671
API Verson: 1.0
{ exploits: 1675,
  auxiliary: 993,
  post: 295,
  encoders: 40,
  nops: 9,
  payloads: 489 
}

굿 JSON 형태로 잘 받아와집니다. 파싱해서 알아서 사용하시면 됩니다. 조금 더 심도있게 봐보죠.

Module 가지고 놀기

API Reference 를 보면.. 굉장히 많은 내용이 있습니다. 우리가 다뤄봐야할 APi 또한 무수히 많지요. 일단 사용하는 형태에 익숙해지면 나머진 참고하면서 개발하면 되니 하나만 골라서 보도록 하겠습니다. 스캔 진행하고, Exploit할 수 있는 module에 대해서 보도록하죠. 각 모듈은 세부 항목으로 나눌 수 있고 info, execute와 같이 실행 / 정보 수집에 관련된 내용도 있습니다.

  • module.exploits
  • module.payloads
  • module.nops
  • module.encoders
  • module.post
  • module.auxiliary
  • module.info
  • module.execute

※ 전부는 아니에요. 더 많아요.. 매우 😊

module.exploits 은 해당하는 모듈 내용(exploit들)을 의미하며 해당 내용만 client.exec로 넘겼을 땐 모듈 exploit 모듈의 정보를 받아오게 됩니다. 각각 하위 항목에 대해서는 JSON 형태로 보이니 적절히 파싱해서 사용하면 되지요. 다른 모듈도 마찬가지입니다. nops, encoders 등등 해당하는 모듈의 이름, 코드, 옵션부터 여러가지 정보를 쉽게 가져올 수 있습니다.

그럼 module.info 를 사용해볼까요? module.info는 이름 그대로 각 모듈에 대한 정보를 가져옵니다. 인자값으론 어떤 모듈 범위인지(exploit, nops, post 등), 상세한 경로는 어떻게 되는지 지정해주고 그에 대한 결과를 JSON으로 받아옵니다.

client.exec(['module.info', 'exploit', 'multi/handler' ])
.then(
  (res)=>{
    console.log(res);
  }
)
.catch(console.log);

이런식으로 데이터를 받아서 출력하는 구문을 넣어주면..

node msgrpc_node.js
MSF Version : 4.15.7-dev-98ba671
API Verson: 1.0
{ type: 'exploit',
  name: 'Generic Payload Handler',
  fullname: 'exploit/multi/handler',
  rank: 'manual',
  disclosuredate: '',
  description: 'This module is a stub that provides all of the features of the Metasploit payload system to exploits that have been launched outside of the framework.',
  license: 'Metasploit Framework License (BSD)',
  filepath: '/home/hahwul/Noon/noon/modules/exploits/multi/handler.rb',
  arch:
   [ 'x86',
     'x86_64',
     'x64',
     'mips',
     'mipsle',
     'mipsbe',
     'mips64',
     'mips64le',
     'ppc',
     'ppc64',
     'ppc64le',
     'cbea',
     'cbea64',
     'sparc',
     'sparc64',
     'armle',
     'armbe',
     'aarch64',
     'cmd',
     'php',
     'tty',
     'java',
     'ruby',
     'dalvik',
     'python',
     'nodejs',
     'firefox',
     'zarch' ],
  platform:
   [ 'Msf::Module::Platform::Android',
....

모듈에 대한 정보를 받아올 수 있죠. module.execute 또한 동일합니다. 인자값으로 모듈에 대한 정보를 넘겨주면.. 해당 모듈을 Metasploit가 아닌 NodeJS에서 msfrpc를 통해 실행할 수 있게되죠.

Client: [ "module.execute", "<token>", "ModuleType", "ModuleName", {
"RHOST" => "1.2.3.4",
"RPORT" => "80"
}
]
Copyright © 2011 Rapid7 LLC | API Reference 33
Server: { "job_id" => 1 }

이런식으로 API 사용과 응용이 가능합니다. 잘 손 본다면 웹 기반의 재미있는 Metasploit 서비스들을 만들 수 있겠네요.

RPC in Ruby

API Reference 보다보니 루비에 대한 이야기가 빠질 수 없습니다. 본래 Metasploit 자체가 루비로 만들어졌기 떄문에 가장 기본이 되는 방법이라고 생각되네요. 메모삼아 같이 남겨둡니다. 당연히 msfrpc에 대한 ruby-gem이 있습니다. 사용을 위해서는 바로 설치해줍니다.

gem install librex --no-rdoc --no-ri
gem install msfrpc-client

첨부터 짜려면 조금 귀찮겠지만 예제 코드가 gemdir 디렉토리 하위에 존재합니다. 디렉토리로 이동한 후 하나하나 구경하시면 됩니다.

cd `gem env gemdir`/gems/msfrpc-client-*/examples

여러개 예제들이 있는데, 그 중 msfrpc_irb.rb를 보도록 하겠습니다.

ruby msfrpc_irb.rb --rpc-user msf --rpc-pass buFdG7be
[*] The RPC client is available in variable 'rpc'
[*] Starting IRB shell...
>>

예제 코드라 별다른건 없고, user, pass를 넘겨 인증 시 irb 상태로 msf 에 접근할 수 있습니다. 혹시라도 irb를 즐겨하시거나 railgun 에 익숙하시다면.. 매우 반갑겠죠.

#> ruby msfrpc_irb.rb –rpc-user msf –rpc-pass buFdG7be –rpc-port 55552 –rpc-host 127.0.0.1 –rpc-uri /api/1.0 –rpc-token [YOUR_TOKEN] [] The RPC client is available in variable ‘rpc’ [] Sucessfully authenticated to the server [*] Starting IRB shell…

인증을 받던, 아니면 직접 토큰을 넘겨주던 해서 IRB 로 접근합니다. msgrpc(msfrpc)와 정상적으로 연결되었네요.

netstat -anp | grep 55552
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
tcp        0      0 127.0.0.1:55552         0.0.0.0:*               LISTEN      -             
tcp        0      0 127.0.0.1:55552         127.0.0.1:38259         ESTABLISHED -             
tcp        0      0 127.0.0.1:38259         127.0.0.1:55552         ESTABLISHED

코드를 보면 이런식입니다.

# Use the RPC option parser to handle standard flags
opts   = {}
parser = Msf::RPC::Client.option_parser(opts)
parser.parse!(ARGV)

puts opts # options.parse를 사용합니다. 이 소리는 직접 인자를 넘겨주지 않아도 아래와 같이 꾸밀 수 있단 이야기겠죠.
#{:user=>"msf", :pass=>"buFdG7be", :port=>55552, :host=>"127.0.0.1", :uri=>"/api/1.0", :token=>"123"}

# 바로 이런식으로!
opts[:user] = "msf"
opts[:pass] = "123"
opts[:port] = "55552"
opts[:host] = "127.0.0.1"
opts[:uri] = "/api/1.0"
opts[:token] = "123"

puts opts

실제로 실행해보면..

ruby msfrpc_irb.rb --rpc-user msf --rpc-pass buFdG7be --rpc-port 55552 --rpc-host 127.0.0.1 --rpc-uri /api/1.0 --rpc-token 123
{:user=>"msf", :pass=>"buFdG7be", :port=>55552, :host=>"127.0.0.1", :uri=>"/api/1.0", :token=>"123"}
{:user=>"msf", :pass=>"123", :port=>"55552", :host=>"127.0.0.1", :uri=>"/api/1.0", :token=>"123"}
[*] Connecting..
[*] Sucessfully authenticated to the server
[*] ID:
[*] PASS:
[*] Starting IRB shell...

잘 되네요. Ruby에서도 msfrpc 형태를 따라가기 떄문에 JSON 형태로 rpc.call()를 통해 명령을 넘겨주면 됩니다.

Conclusion

이 msfrpc 와 ruby, nodejs 라이브러리는 엄청난 가치를 지닌 것들이라고 생각됩니다. 단순히 툴을 사용하는걸 떠나 연동되는 서비스를 만들 수 있고 조금 더 가치있는 부분을 떼어낼 수 있는 좋은 것이라고 생각이 드네요. 물론 API Reference 에 대한 학습은 필수입니다. API만 잘 활용한다면 재미있고 멋진 서비스를 만들어볼 수 있겠네요.

Reference

http://www.nothink.org/metasploit/documentation/RemoteAPI_4.1.pdf https://help.rapid7.com/metasploit/Content/api/rpc/overview.html