ZAP Forced browse 와 Fuzz에서 Sync wordlist 사용하기

Forced Browse는 딕셔너리 기반 또는 단순 브루트포스릍 통해서 서비스에서 경로와 페이지를 식별하는 방법입니다. 아주 전통적이지만 Recon 측면에선 아직도 굉장히 중요한 부분이기도 합니다. 그래서 이러한 작업들을 위해서 기존의 dirsearch, dirbuster 등의 도구부터 최근 feroxbuster, gobuster 등 여러가지 기능과 개선을 적용한 새로운 도구들도 끊임없이 나오고 있습니다.

오늘은 새로운 도구를 소개하는건 아니고 ZAP의 forced browse 기능에 간단한 트릭을 통해 조금 더 영향력 있는 경로를 찾기 위한 방법을 공유해봅니다.

Wordlist?

fuzzer나 discovery 도구 등에서 가장 중요한 역할을 하는건 wordlist 입니다. 모든 글자를 다 포함할 수 있으면 좋겠지만, 네트워크 상의 엄청난 트래픽을 발생시켜야 하기 때문에 유효성이 좋은 word를 기준으로 컴팩트한 wordlist를 사용하는게 좋습니다.

대표적으로 SecList가 있긴 하지만, 최근 추세론 assetnote의 wordlist가 더 인상적이고 결과도 좋은 것 같습니다. (assetnote의 wordlist는 주기적으로 wordlist를 갱신하죠 😁)

또한 동적으로 도메인과 URLs의 특성을 이용하여 wordlist를 만드는 도구들도 굉장히 많이 있습니다.

Custom wordlist for Forced Browse

당연히 ZAP의 forced browse 또한 wordlist를 추가하고 변경하여 사용할 수 있습니다. 물론 다른 cli 도구와 비교해서의 장점은 요청 정보를 바로 ZAP에서 사용할 수 있고 내부적으로 wordlist 파일의 경로를 등록하기 때문에 매번 스캔마다 파일을 지정해줄 필요가 없습니다. (그냥 드롭박스에서 선택!)

wordlist를 추가하는 방법은 간단합니다.

먼저 Options > Forced Browse를 열어주시고,

Add custom forced browse file 를 통해 등록해주시면 끝납니다. 이후엔 forced browse 메뉴에 등록한 워드리스트가 자동으로 나타납니다.

Using sync wordlist for forced browse

물론 좋은 워드리스트는 좋은 결과르 제공해주지만, 때때로 사이트의 특징에 따라 워드리스트를 만들어서 테스트해야할 경우가 있습니다. 그래서 잠깐 고민하다 보니 ln 등의 명령으로 동기화를 해주면 매번 파일을 등록하지 않고 동기 리스트를 가지고 테스트할 수 있겠다 라는 생가기 들었습니다. 그래서 가능한지 알아보기 위해 몇가지를 체크해봤습니다.

How to working?

ZAP의 Forced browse 기능이 어떻게 동작하는지 약간 알아야합니다. 단순히 wordlist의 경로를 참조해서 매번 읽는 형태가 아닌 것 같단 느낌을 받았었거든요(왜냐면 wordlist를 등록하면 installed 되었단 메시지를 주거든요..)

ZAP의 core인지, Add-on인지 확인하기 위해 marketplace를 보니 add-on이네요.

ZAP Add-on은 github 저장소의 위치가 다릅니다. (https://github.com/zaproxy/zap-extensions)

DirBuster를 키워드로 찾아보니 여기인 것 같네요.

Addfile쪽으로 찾아보니, 아래 코드를 만났습니다. 결국 Custom file을 등록하면 매번 그 경로에서 읽어오는게 아닌 시스템 어딘가에 file을 카피해서 만들어두고 사용한다는 것을 알 수 있습니다.

private JButton getAddFileButton() {
 if (addFileButton == null) {
  addFileButton =
   new JButton(Constant.messages.getString(MESSAGE_PREFIX + "button.addfile"));
  addFileButton.addActionListener(
   new java.awt.event.ActionListener() {
   @Override
   public void actionPerformed(java.awt.event.ActionEvent e) {
    JFileChooser fcCommand = new JFileChooser();
    fcCommand.setFileFilter(
     new FileFilter() {
     @Override
     public String getDescription() {
      return Constant.messages.getString(
       MESSAGE_PREFIX + "title");
     }

     @Override
     public boolean accept(File f) {
      return true;
     }
     });

    // Copy the file into the 'home' dirbuster directory
    int state = fcCommand.showOpenDialog(null);

    if (state == JFileChooser.APPROVE_OPTION) {
    FileCopier copier = new FileCopier();
    File newFile =
     new File(
      Constant.getInstance().DIRBUSTER_CUSTOM_DIR
       + File.separator
       + fcCommand.getSelectedFile().getName());
    if (newFile.exists()
     || extension
      .getFileNamesList()
      .contains(newFile.getName())) {
     View.getSingleton()
      .showWarningDialog(
       Constant.messages.getString(
        "bruteforce.add.duplicate.error"));

    } else if (!newFile.getParentFile().canWrite()) {
     View.getSingleton()
      .showWarningDialog(
       Constant.messages.getString(
         "bruteforce.add.dirperms.error")
        + newFile.getParentFile()
         .getAbsolutePath());

    } else {
     try {
     copier.copy(fcCommand.getSelectedFile(), newFile);
     // Refresh list in panel
     extension.refreshFileList();
     // Refresh the list in this popup
     refreshFileList();
     View.getSingleton()
      .showMessageDialog(
       Constant.messages.getString(
        "bruteforce.add.ok"));
     } catch (IOException e1) {
     View.getSingleton()
      .showWarningDialog(
       Constant.messages.getString(
         "bruteforce.add.fail.error")
        + e1.getMessage());
     }
    }
    }
   }
   });
 }
 return addFileButton;
 }
}

(commit)[https://github.com/zaproxy/zap-extensions/blob/d890679f6cea14cbdd1c60c2d9fb6543a671394c/addOns/bruteforce/src/main/java/org/zaproxy/zap/extension/bruteforce/OptionsBruteForcePanel.java]

Find path

자 이제 경로르 찾아봅시다. 위에서 동작 방식을 봤을 때 중요한건 소스코드에서 명시된 DIRBUSTER_CUSTOM_DIR이 실제 파일이 저장될 경로이고, 아마 OS 별로 다 다를 것 같긴 하네요. DIRBUSTER_CUSTOM_DIR은 ZAP Core에 명된 변수 값으로 아래와 같이 ZAP Core repo에서 찾아주면 됩니다.

결국 zapHome+'/dirbuster' 입니다. (소스코드로 실행하시는 분은 clone 경로가 zapHome이고 패키지 설치 버전은 각각 OS와 설치 형태에 따라 다릅니다)

MacOS에서 패키지 설치 버전의 경우 ~/Library/Application Support/ZAP 입니다. 해당 경로는 ps로 쉽게 찾을 수 있습니다.

ps -el | grep ZAP

자 이제 해당 경로에서 보면 dirbuster 경로가 확인됩니다.

ll
total 36576
-rw-r--r--   1 hahwul  staff     0B  3 26  2019 AcceptedLicense
-rw-r--r--   1 hahwul  staff   8.9K  3  1 22:17 add-ons-state.xml
drwxr-xr-x   4 hahwul  staff   128B 12 17 23:34 addOnData
drwxr-xr-x  18 hahwul  staff   576B  8  4  2020 community-scripts
-rw-r--r--   1 hahwul  staff   106K  3  1 23:15 config.xml
-rw-r--r--   1 hahwul  staff    90K 12 17 23:34 config.xml.bak
drwxr-xr-x   4 hahwul  staff   128B  3  1 01:56 dirbuster
...snip...

그리고 들어가서 보면 추가한 파일들이 나타나게 됩니다.

ll dirbuster
total 61720
-rw-r--r--  1 hahwul  staff    30M  3  1 01:52 httparchive_directories_1m_2020_11_18.txt
-rw-r--r--  1 hahwul  staff     9B  3  1 01:56 test.txt

이제 다 끝났습니다. 아까 추가한 파일을 외부에서 동적으로 사용할 파일의 이름으로 심볼릭 링크로 연결해주면, 따로 파일을 등록하지 않아도 갱신된 파일을 사용할 수 있어집니다.

rm -rf /Users/hahwul/Library/Application Support/ZAP/dirbuster/test.txt
ln -s sync_list.txt /Users/hahwul/Library/Application Support/ZAP/dirbuster/test.txt

요렇게 해주면 되겠네요 :D

Fuzz?

~/Library/Application Support/ZAP/fuzzers/dirbuster