Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix ELF argv encoding #1303

Merged
merged 1 commit into from Jan 15, 2023
Merged

Fix ELF argv encoding #1303

merged 1 commit into from Jan 15, 2023

Conversation

elicn
Copy link
Member

@elicn elicn commented Jan 8, 2023

Changed ELF argv encoding from "utf-8" to "latin" to avoid corruption of specially crafted command line arguments (more details in the fixed issue).

Fixes #1269

@ucgJhe
Copy link
Collaborator

ucgJhe commented Jan 9, 2023

// gcc -fno-stack-protector -no-pie -m32 bof.c -o bof.bin

#include <stdio.h>
#include <string.h>

void never_called() {
    puts("qiling power");
}

int fun(char *str) {
    char buffer[20];
    strcpy(buffer, str);
    return 0;
}

int main(int argc, char *argv[]) {
    fun(argv[1]);
    return 0;
}
from qiling import Qiling
from qiling.const import QL_VERBOSE

ql = Qiling(["./bof.bin", "A"*32  + "\x86\x91\x04\x08"], "/", verbose=QL_VERBOSE.DEFAULT)
ql.run()

Platform: 5.15.79.1-microsoft-standard-WSL2 #1 SMP Wed Nov 23 01:01:46 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux

Here is the minimum test script i use to test this PR, and I found out that the output is different from the normal execution. The payload does nothing but simply overwrite saved-eip in stack with another function address for hijacking execution flow.

qiling log

[=]     brk(inp = 0x0) = 0x804f000
[!]     prctl code 0x3001 not implemented
[=]     arch_prctl(code = 0x3001, addr = 0x7ff3cda8) = 0x0
[=]     uname(buf = 0x7ff3ca1a) = 0x0
[=]     access(path = 0x47e25af, mode = 0x0) = -0x1 (EPERM)
[=]     access(path = 0x47e6b5c, mode = 0x4) = -0x1 (EPERM)
[=]     openat(fd = 0xffffff9c, path = 0x47e25c2, flags = 0x88000, mode = 0x0) = 0x3
[=]     statx(dirfd = 0x3, path = 0x47e330a, flags = 0x1800, mask = 0x7ff, buf_ptr = 0x7ff3c128) = 0x0
[=]     close(fd = 0x3) = 0x0
[=]     openat(fd = 0xffffff9c, path = 0x7ff3c210, flags = 0x88000, mode = 0x0) = -0x2 (ENOENT)
[=]     statx(dirfd = 0xffffff9c, path = 0x7ff3c210, flags = 0x800, mask = 0x7ff, buf_ptr = 0x7ff3c0c8) = -0x1 (EPERM)
[=]     openat(fd = 0xffffff9c, path = 0x7ff3c210, flags = 0x88000, mode = 0x0) = -0x2 (ENOENT)
[=]     statx(dirfd = 0xffffff9c, path = 0x7ff3c210, flags = 0x800, mask = 0x7ff, buf_ptr = 0x7ff3c0c8) = -0x1 (EPERM)
[=]     openat(fd = 0xffffff9c, path = 0x7ff3c210, flags = 0x88000, mode = 0x0) = -0x2 (ENOENT)
[=]     statx(dirfd = 0xffffff9c, path = 0x7ff3c210, flags = 0x800, mask = 0x7ff, buf_ptr = 0x7ff3c0c8) = -0x1 (EPERM)
[=]     openat(fd = 0xffffff9c, path = 0x7ff3c210, flags = 0x88000, mode = 0x0) = -0x2 (ENOENT)
[=]     statx(dirfd = 0xffffff9c, path = 0x7ff3c210, flags = 0x800, mask = 0x7ff, buf_ptr = 0x7ff3c0c8) = -0x1 (EPERM)
[=]     openat(fd = 0xffffff9c, path = 0x7ff3c210, flags = 0x88000, mode = 0x0) = -0x2 (ENOENT)
[=]     statx(dirfd = 0xffffff9c, path = 0x7ff3c210, flags = 0x800, mask = 0x7ff, buf_ptr = 0x7ff3c0c8) = -0x1 (EPERM)
[=]     openat(fd = 0xffffff9c, path = 0x7ff3c210, flags = 0x88000, mode = 0x0) = -0x2 (ENOENT)
[=]     statx(dirfd = 0xffffff9c, path = 0x7ff3c210, flags = 0x800, mask = 0x7ff, buf_ptr = 0x7ff3c0c8) = -0x1 (EPERM)
[=]     openat(fd = 0xffffff9c, path = 0x7ff3c210, flags = 0x88000, mode = 0x0) = -0x2 (ENOENT)
[=]     statx(dirfd = 0xffffff9c, path = 0x7ff3c210, flags = 0x800, mask = 0x7ff, buf_ptr = 0x7ff3c0c8) = -0x1 (EPERM)
[=]     openat(fd = 0xffffff9c, path = 0x7ff3c210, flags = 0x88000, mode = 0x0) = 0x3
[=]     read(fd = 0x3, buf = 0x7ff3c390, length = 0x200) = 0x200
[=]     pread64(fd = 0x3, buf = 0x7ff3c140, length = 0x60, offt = 0x1d4) = 0x60
[=]     statx(dirfd = 0x3, path = 0x47e330a, flags = 0x1800, mask = 0x7ff, buf_ptr = 0x7ff3c0c8) = 0x0
[=]     mmap2(addr = 0x0, length = 0x2000, prot = 0x3, flags = 0x22, fd = 0xffffffff, pgoffset = 0x0) = 0x90000000
[=]     mmap2(addr = 0x0, length = 0x2347bc, prot = 0x1, flags = 0x802, fd = 0x3, pgoffset = 0x0) = 0x90002000
[=]     mprotect(start = 0x90022000, mlen = 0x208000, prot = 0x0) = 0x0
[=]     mmap2(addr = 0x90022000, length = 0x182000, prot = 0x5, flags = 0x812, fd = 0x3, pgoffset = 0x20) = 0x90022000
[=]     mmap2(addr = 0x901a4000, length = 0x85000, prot = 0x1, flags = 0x812, fd = 0x3, pgoffset = 0x1a2) = 0x901a4000
[=]     mmap2(addr = 0x9022a000, length = 0x3000, prot = 0x3, flags = 0x812, fd = 0x3, pgoffset = 0x227) = 0x9022a000
[=]     mmap2(addr = 0x9022d000, length = 0x97bc, prot = 0x3, flags = 0x32, fd = 0xffffffff, pgoffset = 0x0) = 0x9022d000
[=]     close(fd = 0x3) = 0x0
[=]     set_thread_area(u_info_addr = 0x7ff3cba0) = 0x0
[=]     set_tid_address(tidptr = 0x90001068) = 0x3967
[=]     set_robust_list(head_ptr = 0x90001070, head_len = 0xc) = 0x0
[!]     0x47ccf83: syscall ql_syscall_rseq number = 0x182(386) not implemented
[=]     mprotect(start = 0x9022a000, mlen = 0x2000, prot = 0x1) = 0x0
[=]     mprotect(start = 0x804b000, mlen = 0x1000, prot = 0x1) = 0x0
[=]     mprotect(start = 0x47ef000, mlen = 0x2000, prot = 0x1) = 0x0
[=]     ugetrlimit(res = 0x3, rlim = 0x7ff3cb94) = 0x0
[=]     statx(dirfd = 0x1, path = 0x901beea7, flags = 0x1800, mask = 0x7ff, buf_ptr = 0x7ff3cb40) = -0x1 (EPERM)
[=]     getrandom(buf = 0x9022fb50, buflen = 0x4, flags = 0x1) = 0x4
[=]     brk(inp = 0x0) = 0x804f000
[=]     brk(inp = 0x8070000) = 0x8070000
[=]     brk(inp = 0x8071000) = 0x8071000
[x]     CPU Context:
[x]     ah      : 0x0
[x]     al      : 0xd
[x]     ch      : 0xd9
[x]     cl      : 0xb4
[x]     dh      : 0x0
[x]     dl      : 0x1
[x]     bh      : 0x41
[x]     bl      : 0x41
[x]     ax      : 0xd
[x]     cx      : 0xd9b4
[x]     dx      : 0x1
[x]     bx      : 0x4141
[x]     sp      : 0xce24
[x]     bp      : 0x4141
[x]     si      : 0xcf04
[x]     di      : 0xb80
[x]     ip      : 0xcf00
[x]     eax     : 0xd
[x]     ecx     : 0x9022d9b4
[x]     edx     : 0x1
[x]     ebx     : 0x41414141
[x]     esp     : 0x7ff3ce24
[x]     ebp     : 0x41414141
[x]     esi     : 0x7ff3cf04
[x]     edi     : 0x47f0b80
[x]     eip     : 0x7ff3cf00
[x]     cr0     : 0x11
[x]     cr1     : 0x0
[x]     cr2     : 0x0
[x]     cr3     : 0x0
[x]     cr4     : 0x0
[x]     cr8     : 0x0
[x]     dr0     : 0x0
[x]     dr1     : 0x0
[x]     dr2     : 0x0
[x]     dr3     : 0x0
[x]     dr4     : 0x0
[x]     dr5     : 0x0
[x]     dr6     : 0xffff0ff0
[x]     dr7     : 0x400
[x]     st0     : 0x0
[x]     st1     : 0x0
[x]     st2     : 0x0
[x]     st3     : 0x0
[x]     st4     : 0x0
[x]     st5     : 0x0
[x]     st6     : 0x0
[x]     st7     : 0x0
[x]     eflags  : 0x6
[x]     cs      : 0x1b
[x]     ss      : 0x28
[x]     ds      : 0x28
[x]     es      : 0x28
[x]     fs      : 0x0
[x]     gs      : 0x63
[x]     Hexdump:
[x]     02 00 00 00 f8 cf f3 7f
[x]     Disassembly:
[=]     7ff3cf00 [[stack]              + 0x02ff00]  02 00                add                  al, byte ptr [eax]
[=]     7ff3cf02 [[stack]              + 0x02ff02]  00 00                add                  byte ptr [eax], al
[=]     7ff3cf04 [[stack]              + 0x02ff04]  f8                   clc
[=]     7ff3cf05 [[stack]              + 0x02ff05]  cf                   iretd
[=]     7ff3cf06 [[stack]              + 0x02ff06]  f3 7f d0             jg                   0x7ff3ced9
[=]     7ff3cf09 [[stack]              + 0x02ff09]  cf                   iretd
[=]     7ff3cf0a [[stack]              + 0x02ff0a]  f3 7f 00             jg                   0x7ff3cf0d
[=]     7ff3cf0d [[stack]              + 0x02ff0d]  00 00                add                  byte ptr [eax], al
[=]     7ff3cf0f [[stack]              + 0x02ff0f]  00 00                add                  byte ptr [eax], al
[=]     7ff3cf11 [[stack]              + 0x02ff11]  00 00                add                  byte ptr [eax], al
[=]     7ff3cf13 [[stack]              + 0x02ff13]  00 10                add                  byte ptr [eax], dl
[=]     7ff3cf15 [[stack]              + 0x02ff15]  00 00                add                  byte ptr [eax], al
[=]     7ff3cf17 [[stack]              + 0x02ff17]  00 d7                add                  bh, dl
[=]     7ff3cf19 [[stack]              + 0x02ff19]  b8 1f 00 06 00       mov                  eax, 0x6001f
[=]     7ff3cf1e [[stack]              + 0x02ff1e]  00 00                add                  byte ptr [eax], al
[=]     7ff3cf20 [[stack]              + 0x02ff20]  00 10                add                  byte ptr [eax], dl
[=]     7ff3cf22 [[stack]              + 0x02ff22]  00 00                add                  byte ptr [eax], al
[=]     7ff3cf24 [[stack]              + 0x02ff24]  11 00                adc                  dword ptr [eax], eax
[=]     7ff3cf26 [[stack]              + 0x02ff26]  00 00                add                  byte ptr [eax], al
[=]     7ff3cf28 [[stack]              + 0x02ff28]  64 00 00             add                  byte ptr fs:[eax], al
[=]     7ff3cf2b [[stack]              + 0x02ff2b]  00 03                add                  byte ptr [ebx], al
[=]     7ff3cf2d [[stack]              + 0x02ff2d]  00 00                add                  byte ptr [eax], al
[=]     7ff3cf2f [[stack]              + 0x02ff2f]  00 34 80             add                  byte ptr [eax + eax*4], dh
[=]     7ff3cf32 [[stack]              + 0x02ff32]  04 08                add                  al, 8
[=]     7ff3cf34 [[stack]              + 0x02ff34]  04 00                add                  al, 0
[=]     7ff3cf36 [[stack]              + 0x02ff36]  00 00                add                  byte ptr [eax], al
[=]     7ff3cf38 [[stack]              + 0x02ff38]  20 00                and                  byte ptr [eax], al
[=]     7ff3cf3a [[stack]              + 0x02ff3a]  00 00                add                  byte ptr [eax], al
[x]     PC = 0x7ff3cf00

[x]     Memory map:
[x]     Start        End          Perm    Label              Image
[x]     0000030000 - 0000031000   rwx     [GDT]
[x]     00047ba000 - 00047bb000   r--     ld-linux.so.2      //lib/ld-linux.so.2
[x]     00047bb000 - 00047e0000   r-x     ld-linux.so.2      //lib/ld-linux.so.2
[x]     00047e0000 - 00047ef000   r--     ld-linux.so.2      //lib/ld-linux.so.2
[x]     00047ef000 - 00047f1000   r--     ld-linux.so.2      //lib/ld-linux.so.2
[x]     00047f1000 - 00047f2000   rw-     ld-linux.so.2      //lib/ld-linux.so.2
[x]     0008048000 - 0008049000   r--     a.out              /home/kerker/qiling/a.out
[x]     0008049000 - 000804a000   r-x     a.out              /home/kerker/qiling/a.out
[x]     000804a000 - 000804b000   r--     a.out              /home/kerker/qiling/a.out
[x]     000804b000 - 000804c000   r--     a.out              /home/kerker/qiling/a.out
[x]     000804c000 - 000804d000   rw-     a.out              /home/kerker/qiling/a.out
[x]     000804d000 - 000804f000   rwx     [hook_mem]
[x]     000804f000 - 0008070000   rwx     [brk]
[x]     0008070000 - 0008071000   rwx     [brk]
[x]     007ff0d000 - 007ff3d000   rwx     [stack]
[x]     0090000000 - 0090002000   rw-     [mmap anonymous]
[x]     0090002000 - 0090022000   r--     [mmap] libc.so.6
[x]     0090022000 - 00901a4000   r-x     [mmap] libc.so.6
[x]     00901a4000 - 0090229000   r--     [mmap] libc.so.6
[x]     0090229000 - 009022a000   ---     [mmap] libc.so.6
[x]     009022a000 - 009022c000   r--     [mmap] libc.so.6
[x]     009022c000 - 009022d000   rw-     [mmap] libc.so.6
[x]     009022d000 - 0090237000   rw-     [mmap anonymous]
Traceback (most recent call last):
  File "/home/kerker/qiling/a.py", line 5, in <module>
    ql.run()
  File "/home/kerker/qiling/qiling/core.py", line 582, in run
    self.os.run()
  File "/home/kerker/qiling/qiling/os/linux/linux.py", line 176, in run
    self.ql.emu_start(self.ql.loader.elf_entry, self.exit_point, self.ql.timeout, self.ql.count)
  File "/home/kerker/qiling/qiling/core.py", line 753, in emu_start
    self.uc.emu_start(begin, end, timeout, count)
  File "/home/kerker/.local/share/virtualenvs/qiling-eadv3MD7/lib/python3.10/site-packages/unicorn/unicorn.py", line 548, in emu_start
    raise UcError(status)
unicorn.unicorn.UcError: Invalid memory read (UC_ERR_READ_UNMAPPED)

strace

execve("./bof.bin", ["./bof.bin", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"...], 0x7ffc13f894c8 /* 50 vars */) = 0
[ Process PID=14776 runs in 32 bit mode. ]
brk(NULL)                               = 0x9871000
arch_prctl(0x3001 /* ARCH_??? */, 0xffaac458) = -1 EINVAL (Invalid argument)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xf7f4b000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 3
statx(3, "", AT_STATX_SYNC_AS_STAT|AT_NO_AUTOMOUNT|AT_EMPTY_PATH, STATX_BASIC_STATS, {stx_mask=STATX_BASIC_STATS|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0644, stx_size=53479, ...}) = 0
mmap2(NULL, 53479, PROT_READ, MAP_PRIVATE, 3, 0) = 0xf7f3d000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/i386-linux-gnu/libc.so.6", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 3
read(3, "\177ELF\1\1\1\3\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0p\27\2\0004\0\0\0"..., 512) = 512
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\4\224\360u\257\274\372\220\4\352\256\334\313\352S\200"..., 96, 468) = 96
statx(3, "", AT_STATX_SYNC_AS_STAT|AT_NO_AUTOMOUNT|AT_EMPTY_PATH, STATX_BASIC_STATS, {stx_mask=STATX_BASIC_STATS|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0644, stx_size=2280756, ...}) = 0
mmap2(NULL, 2312124, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xf7d08000
mprotect(0xf7d28000, 2129920, PROT_NONE) = 0
mmap2(0xf7d28000, 1581056, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x20000) = 0xf7d28000
mmap2(0xf7eaa000, 544768, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1a2000) = 0xf7eaa000
mmap2(0xf7f30000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x227000) = 0xf7f30000
mmap2(0xf7f33000, 38844, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xf7f33000
close(3)                                = 0
set_thread_area({entry_number=-1, base_addr=0xf7f4c500, limit=0x0fffff, seg_32bit=1, contents=0, read_exec_only=0, limit_in_pages=1, seg_not_present=0, useable=1}) = 0 (entry_number=12)
set_tid_address(0xf7f4c568)             = 14776
set_robust_list(0xf7f4c570, 12)         = 0
rseq(0xf7f4ca20, 0x20, 0, 0x53053053)   = 0
mprotect(0xf7f30000, 8192, PROT_READ)   = 0
mprotect(0x804b000, 4096, PROT_READ)    = 0
mprotect(0xf7f88000, 8192, PROT_READ)   = 0
ugetrlimit(RLIMIT_STACK, {rlim_cur=8192*1024, rlim_max=RLIM_INFINITY}) = 0
munmap(0xf7f3d000, 53479)               = 0
statx(1, "", AT_STATX_SYNC_AS_STAT|AT_NO_AUTOMOUNT|AT_EMPTY_PATH, STATX_BASIC_STATS, {stx_mask=STATX_BASIC_STATS|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFCHR|0620, stx_size=0, ...}) = 0
getrandom("\xc6\xcd\x65\x8e", 4, GRND_NONBLOCK) = 4
brk(NULL)                               = 0x9871000
brk(0x9892000)                          = 0x9892000
brk(0x9893000)                          = 0x9893000
write(1, "qiling power\n", 13qiling power
)          = 13
--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_ACCERR, si_addr=0xffaadb00} ---
+++ killed by SIGSEGV +++
fish: Job 1, 'strace ./bof.bin $(echo -ne 'AA…' terminated by signal SIGSEGV (Address boundary error)

Expected output

image

trace log (verbose=DISASM)

image

Also, I noticed that trace log is some how incomplete, the function never_called should be following after ret @ 0x9007536a. I did try to use ql.hook_address on address 0x08049186 and it worked, but it just showing up partially withing log (VERBOSE=DISASM).

@elicn
Copy link
Member Author

elicn commented Jan 10, 2023

I followed the steps you described here, but got the expected result:
image

The never_called function gets executed, then the program crashes when it returns because the strcpy function corrupts the next return address' LSB with the string NULL terminator. I added 'BBBB' after the never_called address to overwrite the next return address as well, and it indeed crashed on fetching from unmapped address 0x42424242:

from qiling import Qiling
from qiling.const import QL_VERBOSE

ROOTFS = r'examples/rootfs/x86_linux'
TARGET = r'/bin/bof2'

ql = Qiling([fr'{ROOTFS}{TARGET}', 'A' * 32 + '\xb6\x91\x04\x08' + 'BBBB'], ROOTFS, verbose=QL_VERBOSE.DEFAULT)
ql.run()

I tried executing this with both Qiling Linux rootfs and my own Linux root, and they both work (that is, display "qiling power" and then crash). So I am not sure there is a problem here; I guess you are using r2 analysis to show the disasm info, could it be the source for the problem..?

As for your final comment about the addresses, I am not sure that I understand the problem. Note that never_called gets executed but it jumps to libc to get the puts function running. In this snippet you just see when it returns, but if you scroll up you'd see the output you are looking for.

@ucgJhe
Copy link
Collaborator

ucgJhe commented Jan 11, 2023

nvm, i got it though. I think this PR is good to merge.

@elicn elicn requested review from kabeor and ucgJhe January 15, 2023 10:01
@kabeor kabeor merged commit 298d7b4 into qilingframework:dev Jan 15, 2023
@elicn elicn deleted the fix-argv branch January 15, 2023 13:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants