How to add custom header in ZAP and zap-cli

The zap-cli is a tool that helps make ZAP easy to use on the command line. From simple scanning to CI/CD Pipeline, it’s a tool that’s used everywhere. Unlike other scanners, it does not support custom headers.

This is also the attribute of ZAP itself, which is basically a user credit-based authentication method. (Actually Burp and other use scanners tend to be somewhat similar.)

So today I’m going to organize how you can use custom headers in ZAP, zap-cli. zap-cli는 ZAP을 커맨드라인에서 쉽게 사용할 수 있도록 도와주는 도구입니다. 단순한 스캐닝부터, CI/CD Pipeline 까지 많은 곳에선 활용되는 도구인데요. 다른 스캐너들과는 다르게 커스텀 헤더를 지원해주지 않습니다.

이는 ZAP 자체가 가지고 있는 속성이기도 한데요, 기본적으로 사용자 Credential 기반의 인증 방식을 사용합니다. (사실 Burp나 다른 사용 스캐너들도 어느정도 비슷한 경향은 있어요)

그래서 오늘은 ZAP, zap-cli에서 커스텀 헤더를 사용할 수 있는 방법에 대해 정리하려고 합니다.

ZAP Command line options

ZAP can use on the various information needed to run an app to options. This allows you to use your personalized ZAP right away.

ZAP은 앱을 실행할 때 실행에 필요한 여러가지 정보를 옵션으로 넘겨줄 수 있습니다. 이를 이용해 개인화된 상태의 ZAP을 바로 사용할 수 있죠.

e.g

./zap.sh -daemon -host 0.0.0.0 -port 4425

(ZAP daemon mode, bind 0.0.0.0:4424)

Options in ZAP Command Line

https://www.zaproxy.org/docs/desktop/cmdline/

But there’s no Custom Header.

No matter how many times I look for the option, there is no data about the header. I looked up the ZAP API, but there was nothing special. So I’ve been thinking about going to Google and I’ve come up with about three ways.

옵션을 아무리 찾아봐도 헤더에 대한 데이터는 없습니다. ZAP API쪽도 찾아봤지만 특별한게 없었습니다. 그래서 구글링해보고 고민해서 3가지 정도 방법을 뽑아냈었죠.

  • Replacer를 cli 에서 활성화하는 방법을 찾는다.
    using replacer on cli
    
  • Passive Scripts를 통해 모든 Req의 헤더를 임의로 수정한다.
    Using passive scripts
    
  • Addon을 만들어서 로딩한다.
    Using addon
    

So I asked Simon. And I heard about the -config option. 그래서 일단 1번 방법이 가장 유력해보여서 Simon에게 물어봤습니다. 그랬더니 -config 옵션에 대한 이야기를 해주더군요.

-config api.key=12345 -config connection.timeoutInSecs=60 -config scanner.strength=MEDIUM

위키에는 잘 나와있진 않지만, -config를 통해 상세한 옵션 조정도 가능한 것 같습니다. 자 그럼 replacer쪽을 한번 봐봅시다.

Replacer

First of all, introduce the Replacer, it is an extension that replaces all requests made by ZAP. I remember it as a built-in, and it allows me to change or generate Req/Res information according to the pattern I specify.

먼저 Replacer에 대한 소개(?)를 하자면, ZAP에서 발생하는 모든 요청에 대해 치환해주는 확장 기능입니다. 제 기억에는 빌트인으로 기억하고 있고, 이를 통해서 지정한 패턴에 따라 Req/Res 정보를 변경하거나 생성할 수도 있죠.

In ‘-config’, the replacer can be controlled in the form shown below. -config 에서 replacer는 아래와 같은 형태로 제어할 수 있습니다.

-config replacer.full_list\(0\).description=desc \
-config replacer.full_list\(0\).enabled=true \
-config replacer.full_list\(0\).matchtype=REQ_HEADER \
-config replacer.full_list\(0\).matchstr=Authorization \
-config replacer.full_list\(0\).regex=false \
-config replacer.full_list\(0\).replacement=1234 \

so, just go code.. 만약 헤더 이름이 기존에 없는 값이라면 새로 생성되어 들어갑니다. 그럼 가볍게 코드로 보면.. 이런식의 구조가 나올 수 있겟네요.

package main

import (
	"log"
	"os/exec"
  "strings"
)

func ParseHeader(h string) (string,string){
  arr := strings.Split(h,": ")
  hn := arr[0]
  hv := arr[1:]
  return hn, strings.Join(hv,":")
}

func main() {
  headerString := "Authorization: abcd1234"
  hn, hv := ParseHeader(headerString)
  options := ` -config replacer.full_list\(0\).description=desc \
  -config replacer.full_list\(0\).enabled=true \
  -config replacer.full_list\(0\).matchtype=REQ_HEADER \
  -config replacer.full_list\(0\).matchstr=`+hn+` \
  -config replacer.full_list\(0\).regex=false \
  -config replacer.full_list\(0\).replacement=`+hv	cmd := exec.Command("/bin/zsh","-c","./zap.sh "+options, "5")
	err := cmd.Start()
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("Waiting for command to finish...")
	err = cmd.Wait()
	log.Printf("Command finished with error: %v", err)
}

log

Found Java version 12.0.1
Available memory: 16384 MB
Using JVM args: -Xmx4096m
0 [main] INFO org.zaproxy.zap.GuiBootstrap  - OWASP ZAP D-2020-05-11 started 08/08/2020, 12:57:30 with home /Users/hahwul/Library/Application Support/ZAP_D/
188 [AWT-EventQueue-0] INFO org.parosproxy.paros.common.AbstractParam  - Setting config replacer.full_list(0).description = desc was Remove CSP
189 [AWT-EventQueue-0] INFO org.parosproxy.paros.common.AbstractParam  - Setting config replacer.full_list(0).enabled = true was false
189 [AWT-EventQueue-0] INFO org.parosproxy.paros.common.AbstractParam  - Setting config replacer.full_list(0).matchtype = REQ_HEADER was RESP_HEADER
189 [AWT-EventQueue-0] INFO org.parosproxy.paros.common.AbstractParam  - Setting config replacer.full_list(0).matchstr = Authorizationhn was Content-Security-Policy

and success! 이후 모든 작업에는 Custom header가 적용됩니다 :D

on zap-cli?

zap-cli’s -o option is sending arguments of zap.sh.You can give a arguments value of sh. zap-cli의 -o 옵션으로 zap.sh의 인자값을 줄 수 있습니다. 결국

zap-cli -o '-config replacer~~~'

같은 형태로 위와 동일하게 만들어줄 수 있겠죠.

References

  • https://www.zaproxy.org/docs/desktop/cmdline/
  • https://www.zaproxy.org/faq/how-do-you-find-out-what-key-to-use-to-set-a-config-value-on-the-command-line/
  • https://groups.google.com/forum/#!topic/zaproxy-users/DGw1GYSy_PI