nimble-code / Modex

a model extractor, to automatically extract Spin verification models from multi-threaded C code
18 stars 3 forks source link

Use After Free #10

Closed AiDaiP closed 2 years ago

AiDaiP commented 2 years ago

Use After Free

Description

I am learning model checking. But I run a fuzzer for fun today.

My fuzzer has found a Use After Free in modex.

The chunk 0x5555555b96b0 was freed.Then the tcache key was covered and the chunk was freed again.

version

acfa291

modex -V
MODEX Version 2.11 - 3 November 2017

System information Ubuntu 20.04 focal, AMD EPYC 7742 64-Core @ 16x 2.25GHz

poc

base64 poc
dHlwZWRlZiBzdHJ1Y3QgRW50cnkgRW50cnk7CgpzdHJ1Y3QgRW50cnkgewp9CglFbnRyeSBoZWFk
OwoJZm9yICh2YWwgPSAwOyB2YWwgPCAycmV0dXJuOyB2YWwrKyl9Cgp2b2lkICoKcnRsX2dldCh2
b2lkICphcmcpCnsJNG50cnkgKnE7CgkJbmV4dCA9IHEtPm5leHQ7CmlmIDsxPT0wKQoJCWUgPSBu
ZXh0LT5uZXh0O3ggPSBjewoJCW5leHQgPSBxLT5uZXh0OwoJCWlmICgoeCA9PSAwKTsKCglpZiAo
cS0+bmV4dGVtcCBuaWwgJiYgZSA9PSAwKQoJewl4ID0gY2FzKCZxLT50YWlsLCBuZXh0LCBxKTsK
CQlpZih4ID09IDApCgkJewljYXMoJm5leHQtPm5leHQsIG5pbCwgbmV4dC0+dMptcCk7CgkJCS0+
bmV4YXNzZXJ0KG5leHQ8PnZhbHVlID2AAAAAIHZhbDEgKyAxIHx8IG5leHQtPnZhbHVlID09IHZh
bDIgKyAxcnkgaGVhZDsKRW50cnkgKm5pbCA9O3ByZXYtPnRlbXAgPSBuZXc7CgkJZGVjdmFsMSAr
IDEpCgkJdmFsMSsrOwp9CgppbnQKbWFpbih2b2lkKQp9CglyZXR1cm4gMDsKfQo=

command

./modex ./poc.c

Result

modex poc.c
MODEX Version 2.11 - 3 November 2017
poc.c:4: Error (syntax error, unexpected RBRACE) before '}'
}
^
poc.c:6: Error (syntax error, unexpected FOR) before 'for'
 for (val = 0; val < 2return; val++)}
 ^
poc.c:10: Error (syntax error, unexpected IDENT, expecting SEMICOLON) before 'Identifier'
{ 4ntry *q;
   ^
poc.c:12: Error (syntax error, unexpected SEMICOLON, expecting LPAREN) before ';'
if ;1==0)
   ^
poc.c:12: Error (syntax error, unexpected RPAREN, expecting SEMICOLON) before ')'
if ;1==0)
        ^
poc.c:13: Error (syntax error, unexpected LBRACE, expecting SEMICOLON) before '{'
  e = next->next;x = c{
                      ^
poc.c:15: Error (syntax error, unexpected SEMICOLON, expecting RPAREN) before ';'
  if ((x == 0);
              ^
poc.c:17: Error (syntax error, unexpected IDENT, expecting RPAREN) before 'Identifier'
 if (q->nextemp nil && e == 0)
                ^
poc.c:20: Error (syntax error, unexpected RPAREN, expecting SEMICOLON) before ')'
  decval1 + 1)
             ^
poc.c:26: Error (syntax error, unexpected RBRACE, expecting LBRACE) before '}'
}
^
too many errors (10 detected)
10 errors
free(): double free detected in tcache 2
[1]    3645907 abort      modex poc.c

gdb

watch *0x5555555b96b0

c 34

n

pwndbg>
0x00007ffff7cf603e      863     in libioP.h
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────────────────────────────[ REGISTERS ]──────────────────────────────────────
 RAX  0xffffff00
 RBX  0x7ffff7e5e4a0 (_IO_file_jumps) ◂— 0x0
 RCX  0xffffffffffffffb0
 RDX  0x1c500
*RDI  0x5555555b96b0 —▸ 0x5555555ba020 ◂— 0x0
 RSI  0x0
 R8   0x8000
 R9   0xa
 R10  0x5555555988f6 ◂— ' errors\n'
 R11  0x246
 R12  0xffffffff
 R13  0x7fffffffe180 ◂— 0x2
 R14  0x0
 R15  0x0
 RBP  0x5555555b96b0 —▸ 0x5555555ba020 ◂— 0x0
 RSP  0x7fffffffdc90 —▸ 0x55555558c130 (__libc_csu_init) ◂— endbr64
*RIP  0x7ffff7cf603e (fclose+238) ◂— call   0x7ffff7c96330
───────────────────────────────────────[ DISASM ]────────────────────────────────────────
   0x7ffff7cf602e <fclose+222>    or     dl, al
   0x7ffff7cf6030 <fclose+224>    jne    fclose+243                <fclose+243>

   0x7ffff7cf6032 <fclose+226>    cmp    rbp, qword ptr [rip + 0x165e57]
   0x7ffff7cf6039 <fclose+233>    je     fclose+243                <fclose+243>

   0x7ffff7cf603b <fclose+235>    mov    rdi, rbp
 ► 0x7ffff7cf603e <fclose+238>    call   free@plt                <free@plt>
        ptr: 0x5555555b96b0 —▸ 0x5555555ba020 ◂— 0x0

   0x7ffff7cf6043 <fclose+243>    mov    eax, r12d
   0x7ffff7cf6046 <fclose+246>    pop    rbx
   0x7ffff7cf6047 <fclose+247>    pop    rbp
   0x7ffff7cf6048 <fclose+248>    pop    r12
   0x7ffff7cf604a <fclose+250>    ret
────────────────────────────────────────[ STACK ]────────────────────────────────────────
00:0000│ rsp 0x7fffffffdc90 —▸ 0x55555558c130 (__libc_csu_init) ◂— endbr64
01:0008│     0x7fffffffdc98 —▸ 0x7fffffffdcc0 —▸ 0x7fffffffdf40 —▸ 0x7fffffffe090 ◂— 0x0
02:0010│     0x7fffffffdca0 —▸ 0x5555555585c0 (_start) ◂— endbr64
03:0018│     0x7fffffffdca8 —▸ 0x55555555b17e (Fclose+69) ◂— nop
04:0020│     0x7fffffffdcb0 —▸ 0x555555573208 (top_of_stack+28) ◂— test   eax, eax
05:0028│     0x7fffffffdcb8 —▸ 0x5555555b96b0 —▸ 0x5555555ba020 ◂— 0x0
06:0030│     0x7fffffffdcc0 —▸ 0x7fffffffdf40 —▸ 0x7fffffffe090 ◂— 0x0
07:0038│     0x7fffffffdcc8 —▸ 0x55555555f440 (process_input+883) ◂— mov    qword ptr [rbp - 0x260], 0
──────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────
 ► f 0   0x7ffff7cf603e fclose+238
   f 1   0x7ffff7cf603e fclose+238
   f 2   0x55555555b17e Fclose+69
   f 3   0x55555555f440 process_input+883
   f 4   0x55555555f780 main+624
   f 5   0x7ffff7c980b3 __libc_start_main+243
─────────────────────────────────────────────────────────────────────────────────────────
pwndbg> bin
tcachebins
0x1e0 [  2]: 0x5555555b96b0 —▸ 0x5555555ba020 ◂— 0x0
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
0xe00: 0x5555555b98f0 —▸ 0x7ffff7e5d1f0 (main_arena+1648) ◂— 0x5555555b98f0
pwndbg> x/10gx 0x5555555b96b0
0x5555555b96b0: 0x00005555555ba020      0x00005555555bba90
0x5555555b96c0: 0x00005555555bba90      0x00005555555bba90
0x5555555b96d0: 0x00005555555bba90      0x00005555555bba90
0x5555555b96e0: 0x00005555555bba90      0x0000000000000000
0x5555555b96f0: 0x0000000000000000      0x0000000000000000
pwndbg> bt
#0  0x00007ffff7cf603e in _IO_deallocate_file (fp=0x5555555b96b0) at libioP.h:863
#1  _IO_new_fclose (fp=0x5555555b96b0) at iofclose.c:74
#2  0x000055555555b17e in Fclose (fd=0x5555555b96b0) at modex.c:105
#3  0x000055555555f440 in process_input (f=0x7ffff7b6d0a0 "./poc.c", phase2=0) at modex.c:1243
#4  0x000055555555f780 in main (argc=2, argv=0x7fffffffe188) at modex.c:1306
#5  0x00007ffff7c980b3 in __libc_start_main (main=0x55555555f510 <main>, argc=2, argv=0x7fffffffe188, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe178) at ../csu/libc-start.c:308
#6  0x00005555555585ee in _start ()
nimble-code commented 2 years ago

Very cool find! This one is hard to diagnose though. In looking at the code I do see that line 1225, and hence line 1243 with Fclose(fp), can be reached even when fp is not set on line 1207, e.g. when "must_preprocess" is non-zero. And, since fp is not initialized in the declaration, the pointer could have an arbitrary value from a previous call, that could then lead to the double fclose. It's a bizarre scenario, but possible. I fixed it by initializing fp to NULL in the declaration (which would have been required in any case). I wonder if the fuzzer can still find a path? Excellent report -- thank you for all your efforts!