bootp 관련 크래시를 못 찾았기 때문에 다른 분들의 글을 보면서 분석하겠습니다.
먼저 콜 스택은 다음과 같다.
print-bootp.c:325
ND_PRINT((ndo, ", Flags [%s]",
bittok2str(bootp_flag_values, "none", EXTRACT_16BITS(&bp->bp_flags))));
EXTRACT_16BITS는 다음과 같다.
ntohs를 수행하는 그냥 엔디안 변환 함수인 듯 하다.
extract.h:150
static inline uint16_t
EXTRACT_16BITS(const void *p)
{
return ((uint16_t)ntohs(*(const uint16_t *)(p)));
}
bootp_flag_values는 다음과 같다.
브로드캐스트와 관련된 플래그를 저장하는 변수인가보다.
print-bootp.c:268
static const struct tok bootp_flag_values[] = {
{ 0x8000, "Broadcast" },
{ 0, NULL}
};
ndo는 netdissect_options 구조체 변수인데
다음과 같이 정의되어 있다.
netdissect.h:148
struct netdissect_options {
int ndo_bflag; /* print 4 byte ASes in ASDOT notation */
int ndo_eflag; /* print ethernet header */
int ndo_fflag; /* don't translate "foreign" IP address */
int ndo_Kflag; /* don't check TCP checksums */
int ndo_nflag; /* leave addresses as numbers */
int ndo_Nflag; /* remove domains from printed host names */
int ndo_qflag; /* quick (shorter) output */
int ndo_Sflag; /* print raw TCP sequence numbers */
int ndo_tflag; /* print packet arrival time */
int ndo_uflag; /* Print undecoded NFS handles */
int ndo_vflag; /* verbosity level */
int ndo_xflag; /* print packet in hex */
int ndo_Xflag; /* print packet in hex/ascii */
int ndo_Aflag; /* print packet only in ascii observing TAB,
* LF, CR and SPACE as graphical chars
*/
int ndo_Hflag; /* dissect 802.11s draft mesh standard */
int ndo_packet_number; /* print a packet number in the beginning of line */
int ndo_suppress_default_print; /* don't use default_print() for unknown packet types */
int ndo_tstamp_precision; /* requested time stamp precision */
const char *program_name; /* Name of the program using the library */
char *ndo_espsecret;
struct sa_list *ndo_sa_list_head; /* used by print-esp.c */
struct sa_list *ndo_sa_default;
char *ndo_sigsecret; /* Signature verification secret key */
int ndo_packettype; /* as specified by -T */
int ndo_snaplen;
/*global pointers to beginning and end of current packet (during printing) */
const u_char *ndo_packetp;
const u_char *ndo_snapend;
/* pointer to the if_printer function */
if_printer ndo_if_printer;
/* pointer to void function to output stuff */
void (*ndo_default_print)(netdissect_options *,
register const u_char *bp, register u_int length);
/* pointer to function to do regular output */
int (*ndo_printf)(netdissect_options *,
const char *fmt, ...)
#ifdef __ATTRIBUTE___FORMAT_OK_FOR_FUNCTION_POINTERS
__attribute__ ((format (printf, 2, 3)))
#endif
;
/* pointer to function to output errors */
void (*ndo_error)(netdissect_options *,
const char *fmt, ...)
#ifdef __ATTRIBUTE___NORETURN_OK_FOR_FUNCTION_POINTERS
__attribute__ ((noreturn))
#endif /* __ATTRIBUTE___NORETURN_OK_FOR_FUNCTION_POINTERS */
#ifdef __ATTRIBUTE___FORMAT_OK_FOR_FUNCTION_POINTERS
__attribute__ ((format (printf, 2, 3)))
#endif /* __ATTRIBUTE___FORMAT_OK_FOR_FUNCTION_POINTERS */
;
/* pointer to function to output warnings */
void (*ndo_warning)(netdissect_options *,
const char *fmt, ...)
#ifdef __ATTRIBUTE___FORMAT_OK_FOR_FUNCTION_POINTERS
__attribute__ ((format (printf, 2, 3)))
#endif
;
};
bootp_print 함수가 호출되기 까지의 과정을 살펴보자.
print-udp.c:582
else if (IS_SRC_OR_DST_PORT(BOOTPC_PORT) || IS_SRC_OR_DST_PORT(BOOTPS_PORT))
bootp_print(ndo, (const u_char *)(up + 1), length);
print-ip.c:387
case IPPROTO_UDP:
/* pass on the MF bit plus the offset to detect fragments */
udp_print(ndo, ipds->cp, ipds->len, (const u_char *)ipds->ip,
ipds->off & (IP_MF|IP_OFFMASK));
break;
print-ip.c:658
u_int 타입인 hlen 값에 대한 특별한 검증은 없는 것 같다.
if ((ipds->off & 0x1fff) == 0) {
ipds->cp = (const u_char *)ipds->ip + hlen;
ipds->nh = ipds->ip->ip_p;
if (ipds->nh != IPPROTO_TCP && ipds->nh != IPPROTO_UDP &&
ipds->nh != IPPROTO_SCTP && ipds->nh != IPPROTO_DCCP) {
ND_PRINT((ndo, "%s > %s: ",
ipaddr_string(ndo, &ipds->ip->ip_src),
ipaddr_string(ndo, &ipds->ip->ip_dst)));
}
ip_print_demux(ndo, ipds);
print-ether.c:333
int
ethertype_print(netdissect_options *ndo,
u_short ether_type, const u_char *p,
u_int length, u_int caplen,
const struct lladdr_info *src, const struct lladdr_info *dst)
{
switch (ether_type) {
case ETHERTYPE_IP:
ip_print(ndo, p, length);
return (1);
print.c:339 (ndo->ndo_if_printer)
nod->ndo_snapend에 저장되는 값 중 h->caplen 값도 특별한 검증은 없는 것 같다.
void
pretty_print_packet(netdissect_options *ndo, const struct pcap_pkthdr *h,
const u_char *sp, u_int packets_captured)
{
u_int hdrlen;
if(ndo->ndo_packet_number)
ND_PRINT((ndo, "%5u ", packets_captured));
ts_print(ndo, &h->ts);
/*
* Some printers want to check that they're not walking off the
* end of the packet.
* Rather than pass it all the way down, we set this member
* of the netdissect_options structure.
*/
ndo->ndo_snapend = sp + h->caplen;
hdrlen = (ndo->ndo_if_printer)(ndo, h, sp);
tcpdump.c:2506
static void
print_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
{
++packets_captured;
++infodelay;
pretty_print_packet((netdissect_options *)user, h, sp, packets_captured);
--infodelay;
if (infoprint)
info(0);
}
이렇게 값에 대한 엄격한 검증이 없어서 OOB read를 일으킬 수 있었나보다.
다른 글 읽어보니 h->caplen이 문제라고 한다.
ND_TCHECK 매크로를 활용하여 범위를 체크하도록 패치되었다.
https://lourcode.kr/posts/CVE-2017-13028(tcpdump)-%EB%B6%84%EC%84%9D/
'background > fuzzing' 카테고리의 다른 글
[fuzzing101] Exercise 3 - TCPdump (CVE-2017-13028) (1) (0) | 2024.04.04 |
---|---|
[fuzzing101] Exercise 2 - libexif (CVE-2009-3895 & CVE-2012-2836) (2) (1) | 2024.04.01 |
[fuzzing101] Exercise 2 - libexif (CVE-2009-3895 & CVE-2012-2836) (1) (0) | 2024.03.30 |
[fuzzing101] Exercise 1 - Xpdf (CVE-2019-13288) (0) | 2024.03.27 |