
사이버 보안 영역에서 웹 서버를 정확하게 식별하고 특성화하는 것은 위협 탐지, 취약점 평가, 네트워크 매핑에 있어 매우 중요합니다. 우리는 다층적 접근 방식을 통해 고유한 서버 프로필을 밝혀내는 새로운 능동 핑거프린팅 알고리즘인 HTTP-Basma를 소개합니다.
HTTP-Basma는 https://httpbasma.netomize.ca/ 에서 서비스 중입니다.
사이버 보안 영역에서 웹 서버를 정확하게 식별하고 특성화하는 것은 위협 탐지, 취약성 평가, 네트워크 매핑에 매우 중요합니다. 우리는 다계층 접근 방식을 통해 고유한 서버 프로필을 밝혀내는 새로운 능동 핑거프린팅 알고리즘인 HTTP-Basma를 소개합니다. 이를 통해 이 문제를 해결합니다.
주요 기능: 제작된 요청, 드러나는 응답: HTTP-Basma는 정교하게 설계된 8개의 HTTP 프로브를 전송하여 서버 구성을 반영하는 독특한 응답을 이끌어냅니다. 다용성을 위한 이중 해싱. 이 알고리즘은 두 가지 해시를 생성합니다:
클러스터링 및 헌팅: 이러한 해시는 서버 클러스터링, 고유 및 유사 서버 식별, 더 높은 신뢰도를 바탕으로 한 악의적 행위자 추적을 가능하게 합니다. 확장을 위한 모듈식 설계: 이 알고리즘의 아키텍처는 새로운 해싱 변형의 추가를 촉진하여 협업과 적응성을 장려합니다.
이 논문에서는 먼저 HTTP 핑거프린팅에 관한 주목할 만한 기존 연구를 살펴본 다음, 알고리즘의 기능, 설계, 아키텍처 및 결과를 탐구합니다. 또한 상위 100만 개 Majestic 웹사이트 스캔에서 얻은 흥미로운 결과를 소개하며, 다양한 악성코드 패밀리의 C&C HTTP 서버 식별 및 클러스터링도 포함됩니다.
HTTP-Basma 알고리즘의 핵심 아이디어는 다양한 요구 사항을 가진 8개의 특수 제작된 HTTP 요청을 전송하여 서버로부터 서로 다른 응답을 유도하는 데 있습니다. 서버 응답을 받으면 HTTP 상태 줄의 모든 요소를 정밀하게 분해하고 최적으로 인코딩합니다. 또한 서버 응답의 일부 헤더도 인코딩 여부를 확인합니다.
전송하는 요청 유형은 다음과 같습니다:
각 요청 후에는 서버 응답을 분석하여 특정 헤더와 해당 값을 추출합니다. 추출된 데이터는 분해 및 인코딩을 포함한 추가 처리를 거쳐 가역적 핑거프린트를 생성합니다.
알고리즘이 작동하는 방식에 대한 전체 기술적 세부 사항은 첨부된 논문에 있습니다.
이 모듈식 설계 철학은 각 요청 핑거프린트를 빌딩 블록으로 취급하여 우아한 리팩터링을 가능하게 하며, 어떤 요청의 핑거프린트든 추가하거나 제거할 수 있습니다.
핑거프린트 샘플:
- verbosus fp: 011420958a0014514bd5221420958a221420958a221420958a2200001420958a22000000001f
- pacto fp: 02464ae8b7d86f82c9918e2c2b9d6b91
- note: false-positive rate (72/986,910)
- verbosus fp: 01142494d60914514bd522142494d6221420958a701420958a220000140e04922032c37f1609
- pacto fp: 020769322f3d94ac2f258ddf5ce08502
- note-1: false-positive rate 0
- note-2: tevedadav.site/43.209.165.126:443 (TLS)
- sample-(sha-256): 9aa1dec8dd12f8adc7fc1274e1958f3613450109ee8b4ec6442a0fcf06df0972
- verbosus fp: 01140a85e40014512f3612140a85e422140a85e422140a85e4220000140a85e4220000000001
- pacto fp: 0207292309a7a7e798e417d69df5f2a5
- note: false-positive rate (73/986,910)
- verbosus fp: 01140a85e4001320958a22142494d62214254c5e2214254c5e22080014254c5e220000000000
- pacto fp: 0202be780e1eaae0eaa6184e20c909b6
- note: false-positive rate (4/986,910)
- verbosus fp: 01140a85e4011320958a22142494d67214254c5e2214254c5e22080014254c5e220000000000
- pacto fp: 02cc5be6d05192e17de041538508bc22
- note: false-positive rate (38/986,910)
- verbosus fp: 01140a85e40914514bd522140a85e4721420958a701420958a220800140a85e4720000001609
- pacto fp: 0221b4e46bbd0e5c037f5a852ca3fdc0
- note: false-positive rate (6/986,910)
HTTP-Basma는 이 알고리즘의 실용성과 실행 가능성을 입증하기 위해 제가 개발한 C++ 도구입니다. 모든 HTTP 소켓 상호 작용에 Chilkat 라이브러리를 활용하며, 해당 라이브러리의 기타 보조 클래스도 사용합니다. 또한 이 도구에는 verbosus 퍼지 해시를 분해하고 역산하여 포괄적인 JSON 객체를 출력하는 디맹글러(demangler) 기능과 두 개의 verbosus 핑거프린트 간의 차이를 출력하는 비교(comparator) 기능이 포함되어 있습니다.
도구의 일부 출력은 약간 다른 프로브 번호를 사용할 수 있지만 기본 순서는 일관되게 유지됩니다: P1->P1, P2->P2, P3->P3, P4->P4, P->P5, P6->P6F, P7->P6L, P8->P7a.
Usage:
HTTP-Basma [OPTION...]
-d, --domain arg domains/IPs (you may query multiple domains, comma separated)
-p, --port arg port number
-s, --ssl does the HTTP connection have to be carried over SSL/TLS?
-q, --qpath check domain with url path included (not recommended)
-w, --redirect enable/disable HTTP redirects. If disabled/false, only the next redirect is followed,
otherwise, all redirects are followed (default: true)
-t, --ctimeout arg socket connection timeout value in seconds (default: 1)
-g, --rtimeout arg socket read (from the server) timeout value in seconds (default: 1)
-e, --sleep arg the duration (in milliseconds) to pause between each request (default: 100)
-x, --proxy arg proxy config: <"socks4|socks5|http">,<domain>,<port>,<bool:direct_tls>,<login>,<pass>
all values are comma-separated. <direct_tls> is ignored with a non-HTTP proxy
-f, --file arg file with list of domains/IPs (requires "-c/--csv" or "-j/--json")
-P, --parallel Scan list of domains passed via the "-f/--file" option in parallel
-c, --csv save to csv file; if the option 'n' is not specified, the CSV filename will be auto
generated
-n, --csvfile arg name of the CSV file
-j, --json save to json file; if the option 'l' is not specified, the JSON filename will be auto
generated
-l, --jsonfile arg name of the JSON file
-r, --saveh save request response headers
-o, --pjson display fingerprint dissection to the console as a JSON object
-i, --demangle_json arg demangle a fingerprint into a detailed json format (you can have more than one, comma
separated)
-u, --demangle_txt arg output a concise text format of the fingerprint, comma-separated for multiple results
-C, --compare arg compare two verbosus fingerprints (comma-separated)
-a, --pacto arg obtain the Pacto fingerprint using Verbosus
-h, --help print usage
특정 도메인/IP를 요청하면 응답을 CSV 또는 JSON 파일로 저장할 수 있으며, 서버 응답 헤더와 각 프로브의 고유 핑거프린트에 대한 다양한 정보가 포함됩니다.
예를 들어, https://google.com 서버의 핑거프린트를 얻고, 각 프로브의 HTTP 응답 헤더를 저장하면서 결과를 JSON 및 CSV 파일로 저장하려면 다음과 같이 실행합니다:
HTTPBasma.exe -d https://google.com --json --csv --saveh
Output 폴더에서 CSV 파일 google_hb_results_2026-05-19_08-35-38_am.csv과 JSON 파일 google_hb_results_2026-05-19_08-35-38_am.json을 찾을 수 있습니다.
이 도구의 디맹글러 기능인 "-i/--demangle_json"은 verbosus 핑거프린트를 입력받아 각 프로브의 속성을 재구성하고 포괄적인 JSON 객체를 출력합니다. 특히 FNV-1a 해시를 역산할 때 디맹글러는 두 개의 로컬 데이터베이스를 사용합니다: 허용된 HTTP 메서드용 options.csv와 상태 줄 사유 구문용 status_line_db.csv입니다. 이 데이터베이스 파일 중 하나라도 없으면 해당 해시 역산 기능이 자동으로 비활성화됩니다. 이 데이터베이스는 상위 100만 개 Majestic 웹사이트 스캔을 통해 컴파일되었습니다.
example.com 도메인에 대한 verbosus 핑거프린트 디맹글링:
HTTPBasma.exe --demangle_json 01140a85e40014514bd522142494d67214254c5e721420958a22020214254c5e720000001609
{
"type": "verbosus",
"fp": "01140a85e40014514bd522142494d67214254c5e721420958a22020214254c5e720000001609",
"p1": {
"type": "get_normal",
"fp": "140a85e400",
"status_line": {
"http_version": {
"fp": "14",
"val_cmt": "HTTP/1.1"
},
"status_code": {
"fp": "0a",
"val_cmt": "200"
},
"http_reason": {
"fp": "85e4",
"val_cmt": "OK"
},
"sl_reversed_db": {
"http_version": "HTTP/1.1",
"status_code": [
200,
404,
403,
500,
204,
999,
888,
603
],
"http_reason": "OK"
}
},
"sts_hdr": {
"fp": "00",
"cmt": "this header is not used"
}
},
"p2": {
"type": "get_invalid_ver_nb",
"fp": "14514bd522",
"status_line": {
"http_version": {
"fp": "14",
"val_cmt": "HTTP/1.1"
},
"status_code": {
"fp": "51",
"val_cmt": "505"
},
"http_reason": {
"fp": "4bd5",
"val_cmt": "HTTP Version Not Supported"
},
"sl_reversed_db": {
"http_version": "HTTP/1.1",
"status_code": [
505
],
"http_reason": "HTTP Version Not Supported"
}
},
"cont_len_hdr": {
"fp": "22",
"name": "Content-Length",
"value": ">1",
"cmt": "content-length/transfer-encoding:chunked header is present with either of the size values: [0,1,>1]"
},
"cnx": {
"ka": false,
"c": true
}
},
"p3": {
"type": "get_rnd_resource",
"fp": "142494d672",
"status_line": {
"http_version": {
"fp": "14",
"val_cmt": "HTTP/1.1"
},
"status_code": {
"fp": "24",
"val_cmt": "404"
},
"http_reason": {
"fp": "94d6",
"val_cmt": "Not Found"
},
"sl_reversed_db": {
"http_version": "HTTP/1.1",
"status_code": [
404,
403,
501,
410,
204,
400,
200,
418
],
"http_reason": "Not Found"
}
},
"cont_len_hdr": {
"fp": "72",
"name": "Transfer-Encoding",
"value": ">1",
"cmt": "content-length/transfer-encoding:chunked header is present with either of the size values: [0,1,>1]"
},
"cnx": {
"ka": true,
"c": false
}
},
"p4": {
"type": "get_rnd_verb",
"fp": "14254c5e72",
"status_line": {
"http_version": {
"fp": "14",
"val_cmt": "HTTP/1.1"
},
"status_code": {
"fp": "25",
"val_cmt": "405"
},
"http_reason": {
"fp": "4c5e",
"val_cmt": "Method Not Allowed"
},
"sl_reversed_db": {
"http_version": "HTTP/1.1",
"status_code": [
405,
403,
204,
418,
404
],
"http_reason": "Method Not Allowed"
}
},
"cont_len_hdr": {
"fp": "72",
"name": "Transfer-Encoding",
"value": ">1",
"cmt": "content-length/transfer-encoding:chunked header is present with either of the size values: [0,1,>1]"
},
"cnx": {
"ka": true,
"c": false
}
},
"p5": {
"type": "get_lowercase_verb",
"fp": "1420958a22",
"status_line": {
"http_version": {
"fp": "14",
"val_cmt": "HTTP/1.1"
},
"status_code": {
"fp": "20",
"val_cmt": "400"
},
"http_reason": {
"fp": "958a",
"val_cmt": "Bad Request"
},
"sl_reversed_db": {
"http_version": "HTTP/1.1",
"status_code": [
400,
422,
405,
401
],
"http_reason": "Bad Request"
}
},
"cont_len_hdr": {
"fp": "22",
"name": "Content-Length",
"value": ">1",
"cmt": "content-length/transfer-encoding:chunked header is present with either of the size values: [0,1,>1]"
},
"cnx": {
"ka": false,
"c": true
}
},
"p6f": {
"type": "get_accept_encoding_full",
"fp": "02",
"cont_enc_hdr": {
"value": "br",
"empty_value": false,
"total_plus": 0
}
},
"p6l": {
"type": "get_accept_encoding_less",
"fp": "02",
"cont_enc_hdr": {
"value": "br",
"empty_value": false,
"total_plus": 0
}
},
"p7a": {
"type": "options_allow_hdr",
"fp": "14254c5e72000000",
"status_line": {
"http_version": {
"fp": "14",
"val_cmt": "HTTP/1.1"
},
"status_code": {
"fp": "25",
"val_cmt": "405"
},
"http_reason": {
"fp": "4c5e",
"val_cmt": "Method Not Allowed"
},
"sl_reversed_db": {
"http_version": "HTTP/1.1",
"status_code": [
405,
403,
204,
418,
404
],
"http_reason": "Method Not Allowed"
}
},
"cont_len_hdr": {
"fp": "72",
"name": "Transfer-Encoding",
"value": ">1",
"cmt": "content-length/transfer-encoding:chunked header is present with either of the size values: [0,1,>1]"
},
"allow_hdr": {
"fp": "000000",
"cmt": "this header is not used"
},
"cnx": {
"ka": true,
"c": false
}
}
}
"status_code" 배열에 여러 HTTP 상태 코드가 포함되어 있음을 확인할 수 있습니다. 이는 서로 다른 서버가 동일한 사유 구문을 다른 상태 코드에 사용할 수 있기 때문에 발생하며, 그 결과 동일한 FNV-1a 해시가 생성됩니다.
비교 옵션 "-C/--compare"는 두 개의 verbosus 핑거프린트를 비교하고 각 프로브의 주요 구성 요소 간 차이를 출력합니다.
예를 들어, Google과 YouTube에 대한 다음 두 핑거프린트를 비교하면:
HTTPBasma.exe --compare 01140a85e4001320958a22142494d62214254c5e2214254c5e22080014254c5e220000000000,01140a85e4011320958a22142494d67214254c5e2214254c5e22080014254c5e220000000000
다음과 같은 출력이 생성됩니다:
< FPrnt-1 Vs. FPrnt-2 >
[ P1 ]
{Strict-Transport-Security}
sts header: 00 != 01
[ P2 ]
[ P3 ]
{Content-Length}
cl_name: 2 != 7
[ P4 ]
[ P5 ]
[ P6F ]
[ P6L ]
[ P7a ]
출력은 P1 프로브의 해시 구성 요소에서 차이를 보여주며, 첫 번째 fp에는 STS 헤더가 있지만 다른 쪽에는 없습니다. 또한 P3 프로브의 경우 두 핑거프린트 간 "Content-Length" 인코딩이 다릅니다.
Netomize는 이 저장소의 공개 코드에 대해 컴파일된 Windows 및 Linux x64 버전을 제공합니다. 또한 majestic 100만 개 HTTP-Basma 핑거프린트 CSV 파일 - 데이터 세트가 첫 번째 릴리스에 포함되어 있습니다.
풀 리퀘스트와 이슈를 환영합니다. 의견과 제안은 언제나 감사히 받겠습니다.