Burp suite Extension 개발에 대한 이야기(Story of Writing Burp suite extension)

최근에 Burp suite Extension 만들고 있던 것 어느정도 완성되다보니 만들면서 느꼈던점들 좀 공유할까 합니다.

완전 처음은 아니지만, 코드량이 생각보다 많아지니 의외로 많은걸 공부하게 되었네요(덕분에 Burp 쪽 API는 확실히 감옵니다. Buby 쓸떄 유용하겠네요)

 

Language

우선 개발 언어입니다. Burp suite는 기본적으로 Java application이기 때문에 확장기능 또한 Java로 구현되며 타 언어로 포팅된 Jruby, Jython을 사용해서 개발도 가능합니다. (환경적으로 지원하는건 이 3가지 입니다)

Java, Jruby, Jython

개인적으로 Java는 별로 안좋아하기 때문에 익숙한 Ruby 기반으로 개발을 시작했었습니다. 처리 로직 부분은 Ruby로 짜서 편하긴한데, JRuby에서 UI 다루는건 정말 최악입니다.. Swing 을 이용해서 처리하는데, Swing 자체는 그렇다 쳐도 Burp쪽에서 제공하는 API 데이터가 Java 기준이라 알아서 바꿔서 써야합니다.

그리서 내린 결론은…

로직이 복잡하면 자신있는 언어(Ruby, Python)로, 보통의 기능의 경우 Java로 짜는게 좋습니다. (저도 결국은 UI에서 고생했죠. 크로스 환경이라 당연히 에러도 에외처리 구문 안주면 아예 안잡힙니다)

Jruby, Jython으로 개발 시 많이 발생하는 문제

위에서도 약간 이야기 드렸지만 Java application을 Ruby로 짜다보니 뜻밖의 문제들이 발생했었습니다.

일단 에러 확인 자체가 무조건 begin-rescue-end 구절로 예외처리 해야 확인이 가능해서 문제가 있을 것 같은 구간에 계속 예외처리 하며 체크를 할 수 밖에 없습니다. (그러다 보면 자연스레 안전한 코드로 바뀌어갈순 있어도.. 많이 복잡해져 보입니다)

두번째는 Object type 관련 부분입니다. Jruby에서 Java type을 사용하는건 크게 문제되진 않는데, Burp에서 구현된 interface나 object를 사용할 때 Object type 때문에 문제가 발생하곤 합니다. 어떤건 Ruby에서 직접 사용 가능하고 어떤건 bytes, 어떤건 to_java로 치환해서 써야하다 보니 코드량이 많아질수록 내가 뭘짜고 있는건지 헷갈려지기 시작합니다.

안그래도 one-line 코드일 가능성이 커서(확장기능이니깐..) 더더욱 심오해지죠.

주로 참고한 Reference site

공식 홈이 가장 좋은 곳이긴 합니다만 모두 Java 기반이기 때문에 필요에 따라 Ruby쪽 type으로 캐스팅해서 사용하셔야합니다. 그래서 그냥 Java로 짜는게 편할 것 같다고 이야기드렸던거구요.

  1. PortSwigger Burp API Reference 우선 가장 좋은 공식 API Reference 입니다.

https://portswigger.net/burp/extender/api/burp/package-summary.html

모든걸 알 순 없지만 아래 주요 Interface로 적어놓은 부분은 꼭 숙지하시는게 좋다고 생각합니다.

  1. Buby Buby는 Burp에서 Ruby를 사용할 수 있게 지원해주는 Extension입니다. Burp API를 직접 호출할 수 있기 때문에 개발할 때 참고하여 사용하기에도 좋고 Buby 자체에서 제공하는 Reference도 많이 유용합니다.

http://tduehr.github.io/buby/doc/frames.html

나머진 다른 확장 기능 코드 참고하면서 진행하면 할만합니다. 위에 2개가 가장 정확하고 좋습니다.

주요 Burp suite interface

튜토리얼처럼 하나하나 설명할 글은 아니여서(너무 많음…) 그냥 꼭 알았으면 하는 interface만 정리해둡니다. 여러가지 형태의 툴을 만드는데 거의 공통적으로 사용되는 부분들입니다.

IBurpExtenderCallbacks

IHttpRequestResponse

IExtensionHelpers

  1. IBurpExtenderCallbacks callbacks은 burp 전반적인 기능들을 담고 있는 callback interface 입니다. request/response에 대한 분석 기능, 신규 request 생성 등 직접 구현하기 귀찮은 부분들이 Method로 정의되어 있어 개발 로드를 많이 줄여줍니다.

  2. IHttpRequestResponse Burp에서 사용하는 Object인데, Request,Response에 대한 정보를 담고있는 객체입니다. 단순하게 request/response 값을 문자로 저장하고 있는게 아닌 여러가지 상태 정보와 Method 들을 같이 담고있어서 UI단 처리가 필요할 때 꼭 잘 알아두셔야 하는 부분입니다. 전 여기서 고생이 제일 많았던 것 같네요.

getComment()
This method is used to retrieve the user-annotated comment for this item, if applicable.
java.lang.String
    
getHighlight()
This method is used to retrieve the user-annotated highlight for this item, if applicable.
IHttpService
    
getHttpService()
This method is used to retrieve the HTTP service for this request / response.
byte[]
    
getRequest()
This method is used to retrieve the request message.
byte[]
    
getResponse()
This method is used to retrieve the response message.
void
    
setComment(java.lang.String comment)
This method is used to update the user-annotated comment for this item.
void
    
setHighlight(java.lang.String color)
This method is used to update the user-annotated highlight for this item.
void
    
setHttpService(IHttpService httpService)
This method is used to update the HTTP service for this request / response.
void
    
setRequest(byte[] message)
This method is used to update the request message.
void
    
setResponse(byte[] message)
This method is used to update the response message.

등...
  1. IExtensionHelpers helpers는 callbacks와 어느정도 유사한 기능을 제공합니다. 우선 callbacks.helper를 가져와서 사용하기 때문에 callback에 종속되어 있긴 하지만, 내부적으로 여러가지 기능을 가지고 있어 대다수 처리 로직에 인자값으로 넘겨주거나 전역화해서 사용하는게 좋습니다. 저는 callbacks, helpers 2개는 거의 인자값으로 넘겨준 것 같네요.

여기도 중요한 method가 많으니 내용 잘 알아두시는게 좋습니다.

Sample code(intruder custom payload)

Burp extender 페이지에서 몇몇개의 Sample 코드를 제공합니다. Java, Jruby, Jython 모두 제공하며 초반에 툴 만드는 프레임 잡기에 괜찮습니다.

https://portswigger.net/burp/extender

Intruder > Custom payload 만드는 코드인데(예제) 보면 대충 어떤게 뭐하는 부분인지 감 오실겁니다. 주석 넣어두었으니 참고하세용

# Burp API 구간은 모두 Java 코드여서 java_import로 불러와야합니다.
java_import 'burp.IBurpExtender'
java_import 'burp.IIntruderPayloadGeneratorFactory'
java_import 'burp.IIntruderPayloadProcessor'
java_import 'burp.IIntruderPayloadGenerator'

# hard-coded payloads
# [in reality, you would use an extension for something cleverer than this]

PAYLOADS = [                               # 아시다싶이 정확한 Payload는 byte 처리가 가장 좋습니다.
  "|".bytes.to_a,                          # byte가 아니면 <svg^Lonload=alert(45)> 이런거 처리하기가 까다롭죠...x0c..
  "<script>alert(1)</script>".bytes.to_a
]

class BurpExtender
  include IBurpExtender, IIntruderPayloadGeneratorFactory, IIntruderPayloadProcessor   # 사요하실 Interface 정해줍니다. 기능이 많아질수록 줄줄이 늘어납니다.

  #
  # implement IBurpExtender
  #

  def registerExtenderCallbacks(callbacks)    # 여기가 실제로 init이 되는 부분입니다. function name은 지정되어 있고..
    # obtain an extension helpers object
    @helpers = callbacks.getHelpers           # 위에서 이야기드렸던 것처럼 callback에서 helper를 받아옵니다.

    # set our extension name
    callbacks.setExtensionName "Custom intruder payloads"

    # register ourselves as an Intruder payload generator
    callbacks.registerIntruderPayloadGeneratorFactory self

    # register ourselves as an Intruder payload processor
    callbacks.registerIntruderPayloadProcessor self
  end

  #
  # implement IIntruderPayloadGeneratorFactory
  #

  def getGeneratorName()
    "My custom payloads"
  end

  def createNewInstance(attack)
    # return a new IIntruderPayloadGenerator to generate payloads for this attack
    IntruderPayloadGenerator.new
  end

  #
  # implement IIntruderPayloadProcessor
  #

  def getProcessorName()
    "Serialized input wrapper"
  end

  def processPayload(currentPayload, originalPayload, baseValue)   # Intruder에서 payload 선택 후 공격 시 processPayload 함수가 호출됩니다.
    # decode the base value
    dataParameter = @helpers.bytesToString(
        @helpers.base64Decode(@helpers.urlDecode baseValue))       # 헐 지금보니깐 helpers 에서 base64ecode 지원하네... 따로 require해서 썼는데

    # parse the location of the input string in the decoded data
    start = dataParameter.index("input=") + 6
    return currentPayload if start == -1

    prefix = dataParameter[0...start]
    end_ = dataParameter.index("&", start)
    end_ = dataParameter.length if end_ == -1

    suffix = dataParameter[end_..-1]

    # rebuild the serialized data with the new payload
    dataParameter = prefix + @helpers.bytesToString(currentPayload) + suffix
    return @helpers.stringToBytes(
        @helpers.urlEncode(@helpers.base64Encode dataParameter))
  end
end

#
# class to generate payloads from a simple list
#

class IntruderPayloadGenerator
  include IIntruderPayloadGenerator

  def initialize()
    @payloadIndex = 0
  end

  def hasMorePayloads()
    @payloadIndex < PAYLOADS.length
  end

  def getNextPayload(baseValue)
    payload = PAYLOADS[@payloadIndex]
    @payloadIndex = @payloadIndex + 1

    return payload
  end

  def reset()
    @payloadIndex = 0
  end
end

각 인터페이스마다 지정된 함수 이름이 있어 맞춰주시면 여러개 형태의 확장 기능도 동시에 올릴 수 있습니다. 제가 만들던것도 여러개 기능이 조합된 형태죠.

Conclusion

Java쪽 Burp Extension들 보면 대다수가 모든 interface를 만들어놓고 사용하더군요. 개발에 편리성은 있겠으나 불필요한 동작이 발생하고 결론적으로 코드를 나누기 까다로운 jruby나 jython의 경우 시도하기 좀 껄끄러운 방식입니다. 되도록이면 Java로 구현하는걸 추천하되 이종 언어간 개발이나 삽질을 경험해보고 싶다면 Jruby, Jython 추천드립니다.

나아아아아아아아아 중에 시간나면 개인 PJT로 하나 만들어서 올려보것습니다. (기대 마셔요. 밀린 PJT가 한두개가 아니라 … ㅋㅋㅋㅋㅋㅋ, 심지어 a2sv 갑자기 문의가 막 들어오고 난리, 난 회사 일하기도 바쁜데)