Hello,

When using a vulnerability scanner on haproxy 1.7.5, we discovered a scenario 
under which the haproxy segfaults.

Unfortunately, this is a "bundled" scanner whith no access to the exact 
requests, and the haproxy terminates the SSL for https, so not easy to capture 
the actual traffic, but we managed to gather a core of haproxy.

This happens only when using the cookie SERVERID for session stickiness:

The backtrace is as follow:

I believe the scanner injects a screwed up header and/or cookie. The result 
appears to be the incorrect memmove of the last header line :

(gdb) bt full
#0  _wordcopy_fwd_dest_aligned (dstp=14237664, srcp=14237696, 
len=2305843009213653720) at wordcopy.c:196
        a0 = <value optimized out>
        a1 = <value optimized out>
        a2 = 0
        a3 = 0
        sh_1 = 40
        sh_2 = 24
#1  0x0000003727a838be in memmove (dest=0xd4575d, src=<value optimized out>, 
len=18446744073709551439) at memmove.c:73
        dstp = <value optimized out>
        srcp = <value optimized out>
#2  0x0000000000411217 in buffer_replace2 (b=0xd456b0, pos=0xd4575d 
"Accept-Encoding: gzip\r\n\r\n.32.31\r\nConnection: close\r\n\r\nre\r\n\r\n", 
end=<value optimized out>, str=0x0, len=0) at src/buffer.c:90
        delta = -21
#3  0x000000000045de02 in manage_client_side_cookies (s=0xcfa800, req=0xcfa810) 
at src/proto_http.c:7976
        delta = <value optimized out>
        cur_hdr = 0xcfac6c
        val = <value optimized out>
        txn = <value optimized out>
        sess = 0xc1c930
        preserve_hdr = 0
        cur_idx = 3
        old_idx = 2
        hdr_beg = 0xd4575d "Accept-Encoding: gzip\r\n\r\n.32.31\r\nConnection: 
close\r\n\r\nre\r\n\r\n"
        hdr_end = 0xd45770 "ip\r\n\r\n.32.31\r\nConnection: 
close\r\n\r\nre\r\n\r\n"
        hdr_next = 0xd45772 "\r\n\r\n.32.31\r\nConnection: 
close\r\n\r\nre\r\n\r\n"
        del_from = 0xd45763 "-Encoding: gzip\r\n\r\n.32.31\r\nConnection: 
close\r\n\r\nre\r\n\r\n"
        prev = <value optimized out>
        att_beg = <value optimized out>
        att_end = <value optimized out>
        equal = <value optimized out>
        val_beg = <value optimized out>
        val_end = <value optimized out>
        next = <value optimized out>
#4  0x0000000000460a86 in http_process_req_common (s=0xcfa800, req=0xcfa810, 
an_bit=256, px=0x86a650) at src/proto_http.c:4474
        sess = 0xc1c930
        txn = 0xcfab50
        msg = 0xcfabb0
        rule = <value optimized out>
        wl = <value optimized out>
        verdict = <value optimized out>
        deny_status = 2
#5  0x0000000000486d0e in process_stream (t=0xb8fa70) at src/stream.c:1798
        max_loops = 199
        ana_list = 2304
        ana_back = 2304
        flags = 2
        srv = <value optimized out>
        s = 0xcfa800
        sess = 0xc1c930
        rqf_last = <value optimized out>
        rpf_last = 2147745792
        rq_prod_last = <value optimized out>
        rq_cons_last = <value optimized out>
        rp_cons_last = 7
        rp_prod_last = 0
        req_ana_back = <value optimized out>
        req = 0xcfa810
        res = 0xcfa850
        si_f = 0xcfaa38
        si_b = 0xcfaa60
#6  0x0000000000415ac0 in process_runnable_tasks () at src/task.c:238
        t = <value optimized out>
        max_processed = <value optimized out>
#7  0x0000000000407028 in run_poll_loop () at src/haproxy.c:1724
        next = <value optimized out>
#8  0x000000000040a308 in main (argc=<value optimized out>, argv=<value 
optimized out>) at src/haproxy.c:2105
        err = <value optimized out>
        retry = <value optimized out>
        limit = {rlim_cur = 4091, rlim_max = 4091}
        errmsg = 
"\000\347K\000\000\000\000\000\b\300n\000\000\000\000\000\020\340\377\377\377\177\000\000\000\300n\000\000\000\000\000\006\000\000\000\000\000\000\000H\341\377\377\377\177\000\000\200\341\377\377\377\177\000\000XpA\000\000\000\000\000\000\300n\000\000\000\000\000\226\070K\000\000\000\000\000\340\070\370/7\000\000\000\340\067K\000\000\000\000\000\000\000\000"


Basically, the memove in #1 is called with len=18446744073709551439 from 
buffer_replace2, which is a negative value.

I am not sure exactly in which case this is possible (last line of the header 
incorrect or something), but bi_end(b) is < to end, so the unsigned size_t 
expected by memmove is incorrect.

I recompiled haproxy with the patch below and now it survives the vulnerability 
scanner, but it might not be at the proper place of the code (maybe the logic 
fault is better addressed above in manage_client_side_cookies... ):

diff -uNr haproxy-1.7.5/src/buffer.c haproxy-1.7.5p/src/buffer.c
--- haproxy-1.7.5/src/buffer.c  2017-04-03 10:28:32.000000000 +0200
+++ haproxy-1.7.5p/src/buffer.c 2017-05-26 13:04:58.225311000 +0200
@@ -87,7 +87,8 @@
                return 0;  /* no space left before wrapping data */

        /* first, protect the end of the buffer */
-       memmove(end + delta, end, bi_end(b) - end);
+       if ( bi_end(b) > end )
+               memmove(end + delta, end, bi_end(b) - end);

        /* now, copy str over pos */
        if (len)


the configuration is as follows:



frontend ft_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_443
    bind 185.139.245.111:443 ssl crt 
/etc/haproxy/ssl/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.pem   ciphers 
AES256+EECDH:AES256+EDH
    bind-process 2 3 4
    mode http
    option http-buffer-request

    http-response set-header Strict-Transport-Security "max-age=16000000; 
includeSubDomains; preload;"
    http-request del-header Origin

    stick-table type ip size 1m expire 1m store gpc0,conn_rate(1s)

    tcp-request connection track-sc1 src

    tcp-request connection reject if { sc1_get_gpc0 gt 0 }

    acl abuse_tcp_conn sc1_conn_rate(ft_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_443) 
ge 80
    acl flag_tcp_conn_abuser 
sc1_inc_gpc0(ft_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_443) gt 0
    tcp-request content reject if abuse_tcp_conn flag_tcp_conn_abuser

    tcp-request connection reject if { 
sc1_get_gpc0(XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_8443) gt 1 }

    default_backend XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_8443
    maxconn 5000
    timeout client 5s
    log global

backend XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_8443
        mode http
        balance roundrobin
        option forwardfor

        stick-table type ip size 1m expire 1m store 
gpc0,http_req_rate(1s),http_err_rate(1s)
        http-request track-sc2 src
        acl abuse_too_many_errors 
sc2_http_err_rate(XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_8443) ge 20
        acl flag_abuser_too_many_errors 
sc2_inc_gpc0(XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_8443) gt 0
        reqtarpit . if abuse_too_many_errors flag_abuser_too_many_errors
        reqtarpit . if { sc2_get_gpc0 gt 0 } flag_abuser_too_many_errors

        timeout check 10s
        timeout check 5s

        option httpchk HEAD /XXXXXXXXX/ HTTP/1.1\r\nHost:localhost

        cookie SERVERID insert indirect nocache
        server ppmktplportals01fe 172.21.38.22:8443 check ssl verify none check 
cookie ppmktplportals01fe
        server ppmktplportals01fv 172.21.38.100:8443 check ssl verify none 
check cookie ppmktplportals01fv


Let me know if I can help more,

Best Regards,

- Jean
This email and its content belong to Ingenico Group. The enclosed information 
is confidential and may not be disclosed to any unauthorized person. If you 
have received it by mistake do not forward it and delete it from your system. 
Cet email et son contenu sont la propriété du Groupe Ingenico. L’information 
qu’il contient est confidentielle et ne peut être communiquée à des personnes 
non autorisées. Si vous l’avez reçu par erreur ne le transférez pas et 
supprimez-le.

Reply via email to