DIR-2640_REVA_FIRMWARE_1.11B02

After extracting the firmware using binwalk, drag prog.cgi into IDA for analysis:

image

In the sub_481FC8 function, we can see a variable count controlling the number of iterations of a for loop. Within the loop, the strcpy function is used to copy haystack into ptr. However, since count is not validated, if count is sufficiently large, it can cause a heap overflow.

Checksec Analysis:

image

It turns out that no protections are enabled. First, let’s test it under QEMU in user mode. Run prog.cgi:
image

It seems some devices are not mounted, so let’s first start a chroot container and run rcS:

1
2
chroot . ./qemu-mipsel-static ./bin/ash
cd /etc/init.d && ./rcS

image

Although there are some issues, some disks are successfully mounted. Now let’s try running prog.cgi again:

image

It throws an error. After debugging, we found that the program exits at the sub_42D2DC function. This function is a registry function that triggers an ioctl error when calling the trace function. Therefore, we patched it, successfully entering the main loop:

image

Later, we discovered that this program does not communicate over the network but instead uses the getenv function to interact with the web application via environment variables. Despite various attempts, the author could not successfully launch the entire web application, so another approach was needed.

How to Make the Program Crash:

The vulnerable code is as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
ptr = malloc(0xFFF0u);
if ( ptr )
{
memset(ptr, 0, 2184 * v45);
Count = webGetCount(a1, "/SetScheduleSettings/ScheduleInfoLists#");
if ( !Count )
Count = 0;
for ( i = 0; i < Count; ++i )
{
memset(v50, 0, sizeof(v50));
snprintf(v50, 0x200u, "/SetScheduleSettings/ScheduleInfoLists:%d/%s", i, "ScheduleName");
haystack = (char *)webGetVarString(a1, (int)v50);
if ( !haystack )
{
v26 = '\v';
goto LABEL_87;
}
if ( !*haystack )
{
sub_4812CC(v45);
goto LABEL_78;
}
strcpy((char *)ptr + 0x888 * i + 4, haystack);
...
}
...
}

To trigger the loop and reach strcpy, two conditions must be satisfied:

  1. webGetVarString must return a valid value.
  2. *haystack must not be empty.

Once these two conditions are met, the strcpy function will be called within the loop, copying up to 0x888 bytes from haystack into ptr. To keep the loop running, observe the following part of the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
memset(v50, 0, sizeof(v50));
snprintf(v50, 0x200u, "/SetScheduleSettings/ScheduleInfoLists:%d/ScheduleInfo#", i);
v46 = webGetCount(a1, v50);
if ( strstr(haystack, "-UH1xLTJa7O") )
{
v37 = 0;
v52[0] = ++v36 + 1;
v35 = 1;
strncpy((char *)&v52[761 * v36 + 2], haystack, 0x10u);
v52[761 * v36 + 1] = 0;
}
else
{
v35 = 0;
}
if ( v46 ){
...
}
...

To simplify the model, set v46 to 0 so the if branch is skipped. After entering else, the program will proceed to the next iteration of the loop, achieving our objective.

Debugging Method:

Copy qemu-mipsel-static to the root directory of the firmware. Start the chroot container and run the following commands:

1
2
3
4
cp /usr/bin/qemu-mipsel-static .
chroot . ./qemu-mipsel-static ./bin/ash
cd /etc/init.d && ./rcS
cd / && ./qemu-mipsel-static -g 1234 ./bin/prog.cgi

In another terminal, start gdb-multiarch:

1
2
3
4
5
6
gdb-multiarch
# Note: The author used the pwndbg plugin for GDB, but gef should also work.
pwndbg> set architecture mips
pwndbg> file ./bin/prog.cgi
pwndbg> target remote localhost:1234
# Then set a breakpoint inside the main function.

Hooking into the sub_481FC8 Function:

Write a main_hook.so library to hook into the sub_481FC8 function using libc_start_main. Provide an arbitrary input, setting count to 64. Below is seeds.bin:

1
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

The I/O hook function is as follows:
image

With 64 uppercase ‘A’s, set LD_PRELOAD to main_hook.so and run the following command:

1
chroot . ./qemu-mipsel-static -L . -E LD_PRELOAD=/lib/libdl.so.0:./stack_hook.so -g 1234 ./bin/prog-cgi seeds.bin > log.txt

Debug further, and notice a segmentation fault:
image

Upon inspection, it turns out the haystack string overwrote the ptr below it, causing an error when dereferencing ptr. In short, this is a buffer overflow that allows arbitrary write primitives.

The stack structure is as follows:
image