Zest와 ZAP을 이용한 Semi-Automated Security Testing

What is Zest script

Zest script는 ZAP에서 제공하는 스크립팅 언어로 ZAP 내부에서의 요청과 처리 등 많은 기능을 JSON 기반의 스크립트화 하고 사용/관리할 수 있는 언어입니다. BurpSuite 등 다른 메뉴얼 테스팅 도구에서는 없는 ZAP만의 강점인 기능이죠.

  • JSON 포맷
  • Record를 통해 쉽게 스크립트를 기록할 수 있음
  • ZAP 내부의 변수 사용 등으로 동적인 테스트가 가능함
  • Zest script 파일을 공유하여 타 사용자와도 자동화 테스팅 패턴을 공유하여 사용할 수 있음

Record and using

Zest script는 JSON 포맷으로 직접 작성하여 사용할 수 있지만, ZAP에서 Record 기능을 통해 쉽게 자동 작성할 수 있으니 되도록이면 Record를 이용하여 작성하는게 좋습니다. (노가다를 굉장히 많이 줄일 수 있어요)

Record를 이용하여 신규 Zest script 생성하기

신규 Zest script를 생성할 땐 메뉴바의 Record 아이콘을 클릭하여 Record를 시작할 수 있습니다.

클릭 시 팝업이 발생하며 신규로 생성할 Zest script의 정보를 추가할 수 있습니다.

  • Title: 스크립트 이름
  • Type: 스크립트 타입 / 중요한건 아니고, Scripts에 표기될 떄 저장될 카테고리 정도로 생각하시면 됩니다.
  • Record Type: 정확하겐 모르겠네요..
  • Initial URL: Record 할 최상위 URL / 없으면 전체를 Record 합니다.
  • Prefix: Inital URL과 비슷
  • Load on start: ZAP 부팅 시 포함할껀지
  • Description: 설명

Default Assertions는 아래 Assertions 부분에 대한 이야기입니다. 그냥 기본값으로 사용하셔도 무방합니다. 아무튼 저장하시면 이후부턴 request 들이 record됩니다. 자동화하실 페이지나 기능들을 요청하면 Zest script에 자동으로 추가됩니다.

불필요한 요청은 나중에 제거 해야겠죠.

이후에 메뉴바의 Record 아이콘을 다시 클릭하게 되면 기록이 종료됩니다.

기존 Zest script에 Record를 이용하여 추가하기

만약 Zest script를 작성하던 중 추가로 Request를 더 넣고 싶은 경우에 아래와 같이 해당 Zest script에서 우클릭 후 Start recording 으로 동일하게 record할 수 있습니다.

기록된 Zest script 실행해보기

Script Console 탭에서 사용할 Zest script가 선택된 상태에서 Run 을 눌러주시면 스크립트가 동작합니다. 이는 하단 탭의 Zest Results에서 결과를 확인할 수 있습니다.

Zest structure

Zest는 정말 여러가지 기능들을 제공해주고 있습니다. 그 중 2가지에 대해 간단하게 설명 드릴까 합니다.

Assertions

Zest Assertions are ‘sanity checks’ associated with requests and applied to responses. A failing assertion should typically be an indication that the test is not working as expected - either the script is incorrect or the script has not been configured correctly for the target application.

Assertions는 Zest에서 원래 정상 수행을 체크하기 위해 만든 기능입니다. status code, response size 등 조건을 명시해두고 이를 충족하지 않았을 때, 즉 비정상 동작한 경우 Zest script 결과에 표기해주어 사용자가 상태를 파악할 수 있도록 도움을 줍니다.

그리고 이 기능을 잘 활용하면 다른 테스팅 도구들이 특정 패턴에 따라 탐지하는 방식과 같이 구현하여, 스캔성의 작업도 충분히 진행할 수 있습니다. (권한 별 접근 권한 검사할 때 편리합니다)

예시를 들어보면, Status code가 200이며, Original Request와 Size 차이가 98퍼 이상(아래 코드에선 62590) 일치하고, 본문에 HAHWUL이란 문자열이 있는 경우 Zest Results에 체크 표시가 나타나게 됩니다.

"assertions": [
        {
          "rootExpression": {
            "code": 200,
            "not": false,
            "elementType": "ZestExpressionStatusCode"
          },
          "elementType": "ZestAssertion"
        },
        {
          "rootExpression": {
            "length": 62590,
            "approx": 2,
            "variableName": "response.body",
            "not": false,
            "elementType": "ZestExpressionLength"
          },
          "elementType": "ZestAssertion"
        },
        {
          "rootExpression": {
            "regex": "HAHWUL",
            "variableName": "response.body",
            "caseExact": false,
            "not": false,
            "elementType": "ZestExpressionRegex"
          },
          "elementType": "ZestAssertion"
        }
      ]

Conditionals(IF/THEN/ELSA)

Zest Conditionals are a generic term for statements that provide IF / THEN / ELSE functionality. All conditional statements test for a particular condition and provide 2 alternate paths. Each path may contain zero or more statements, and these can include conditionals to any depth.

전형적인 분기분입니다. Conditionals를 추가하면 타 언어와 동일하게 if, then, else 를 지정하여 Zest script가 처리할 로직을 만들어갈 수 있습니다.

아래를 예시로 들어보면, status code가 404인 경우, github.com으로 요청 / 아닌 경우 google.com으로 요청합니다.

{
      "rootExpression": {
        "code": 404,
        "not": false,
        "elementType": "ZestExpressionStatusCode"
      },
      "ifStatements": [
        {
          "url": "https://github.com",
          "data": "",
          "method": "GET",
          "headers": "",
          "assertions": [],
          "followRedirects": true,
          "timestamp": 0,
          "cookies": [],
          "index": 3,
          "enabled": true,
          "elementType": "ZestRequest"
        }
      ],
      "elseStatements": [
        {
          "url": "https://google.com",
          "data": "",
          "method": "GET",
          "headers": "",
          "assertions": [],
          "followRedirects": true,
          "timestamp": 0,
          "cookies": [],
          "index": 4,
          "enabled": true,
          "elementType": "ZestRequest"
        }
      ],
      "index": 2,
      "enabled": true,
      "elementType": "ZestConditional"
    }

Action / Assignments / Client / Loop and etc…

이외에도 정말 많은 기능들이 있습니다. 모두 어렵고 복잡한 개념은 없으니 zest 위키쪽 찾아서 읽어보시면 활용하시는 데 있어 많은 도움이 됩니다.

Conclusion

BurpSuite의 Intruder, Turbo Intruder 및 다른 Fuzzer 들과 다르게 Zest를 이용한 자동화 테스팅은 Request에 대한 많은 정보를 활용하고, 시퀀스를 저장하여 원하는 로직에 따라 재생하고 테스트할 수 있기 때문에 스크립트를 구성하는 상태에 따라서 정말 말도안되게 유연하고 복잡한 작업에도 사용할 수 있습니다.

개인적으론 아래 부분에서 많이 사용될 것 같고, 이 모든 행위에 대한 Zest script 또한 파일로 저장하고 다른 사용자의 ZAP에서도 쓸 수 있기 때문에 공유하여 이러한 과정들을 단축시킬 수 있습니다. 그리고 취약점을 찾는 행위 이외에도 컴플라이언스적인 테스팅이나 복잡한 비즈니스 로직 테스팅에도 사용할 수 있다는 점이 가장 메리트로 다가오네요.

  • 인증/권한부여 관련 자동화 테스팅
  • 비즈니스 로직 테스팅
  • 자동 로그인
  • 복잡한 형태의 반복 테스팅

References