LOADING

加载过慢请开启缓存 浏览器默认开启

2025UTCTF_Re Writeup

Ostrich Algorithm

还可以在汇编里面把call exit nop掉,直接动调到最后可以拿到md5值

image-20250315124146972

Safe Word

逻辑上可能有点不好理解,我们先理一下逻辑。

下面是main函数和一个sub_11e9()和sub_11fc(arg1),使用了BN反编译。

image-20250316231654996

image-20250316231440997

main函数开头使用了一个mmap函数分配了一块可以用于执行的内存,也就是说data_16020中的内容可以被作为代码执行。我们的输入被放入了rax_2中。并且还初始化了一个值var_20=0x5b。然后以我们的输入和var_30计算出索引获取rax_5中的值,作为参数传入sub_11fc()中,在上面分析sub_11fc()函数其实就是把rax_5的值作为了函数(代码)执行。既然是作为指令执行。那么这肯定就是是汇编的字节码,这四字节数据是一段汇编代码,被sub_11fc()执行,并把返回值赋给var_20用作下一次索引的计算。依次遍历完我们的输入。

for i in range(32):
    var_20 = f( rax5[ (input[i] + var_20 << 8) << 3 ] )

我们的输入在一次的循环中是可以不同的,范围是(32–>127),而var_20在一次循环中是不变的,有没有想过var_20的本质是什么?var_20在每次计算时都被左移了8位,假设var_20是0xff那么 0xff<<8 == 0xff00;此时与input[i]相加刚好构成了一个范围(0xff20–>0xff7f)。那么外面的”<< 3”是干嘛的?观察一下rax_5的索引间隔是8,”<<3”其实就是乘8,所以我们计算出的值要*8才是rax_5的索引。每次执行完代码后都会更新var_20的值,于是范围发生了改变,假设改变后的var_20 = 0x11于是范围改变,(0xff20–>0xff7f) –> (0x1120–>0x117f)。有没有一种感觉,rax_5被分成了许多的块,而var_20其实就是某一个块的索引,每一个块中又会指定下一个块的索引?再想一下,这和函数的跳转是不是有异曲同工之处?看一下rax_5的值发现确实是分块的。我们知道flag的开头是”utflag{“不妨动调看看执行的指令到底是什么。在call处下断点进去查看。再继续运行几次看看,发现指令形式也是一样的。

image-20250317001514509

函数直接返回了0x0B,也就是下一个块的索引,这下就真相大白了。

其实主要的逻辑就是函数跳转,var_20是函数的基地址,我们要用输入的字符控制函数执行指令的地址(基地址+字符),使函数直接跳转到下一个函数,只要遍历完所有的函数,我们的输入就是flag。

我们可以计算出每一段函数的基地址,然后遍历所有可见字符,如果匹配到”0x6a, l[i + 1], 0x58, 0xc3(push xx pop rax retn)”这条指令,说明这个字符就是正确的(这里的执行顺序就是函数的排布顺序)。

那么这题目的意义是什么呢?我们的输入可以看作是一个漏洞,我们可以通过构造一个输入来控制函数的执行流程(控制流入侵),达到自己想要的目的,那么这就完成了一次入侵。我们逆向自己的pwn题


def parse_data(data):
    entries = data.strip().split(";")
    values = dict()
    for entry in entries:
        if "=" in entry:
            str = entry.split("=")
            index  = str[0].strip()[3::].replace(']','')
            value = str[1].strip()
            values[int(index)]=list(int(value.replace("LL",'')).to_bytes(4,"little"))
    return values

data = """
  v7[23296] = 1933468268LL;
  v7[23297] = 729031286LL;
  v7[23298] = 2575079303LL;
  v7[23299] = 1475628389LL;
  v7[23300] = 243385641LL;
  v7[23301] = 1799261851LL;
 ......
  """

code = parse_data(data)
l = []
op = 91
path = []
path.append(91)

i =1

for j in code:
    dex = j // 256
    if dex == path[i-1] : continue
    path.append(dex)
    i+=1

print(path)

flag =''
for l in range(len(path)):
    for char in range(32,128):
        try:
            if code[(char + op * 256)]==[0x6a,path[l+1],0x58,0xc3]:
                flag += chr(char)
                op = path[l+1]
                break
        except:
            continue

print(flag,len(flag),len(path))

#utflag{1_w4nna_pl4y_hypix3l_in_c}

map

就是把字符用五个数字表示,输入所有可见字符然后导出表就行。

text =""

for i in range(32,127):
    text += chr(i)
print(text)

str1 = text[:35:]
str2 = text[35:35+35:]
str3 = text[35+35::]

print(str1,str2,str3)

table = dict()
output1 ="4916849171491704917349172491754917449161491604916349162491654916449167491664915349152491554915449157491564915949158492094920849211492104921349212492154921449201492004920349265"
output2 = "4920249205492044920749206491934919249195491944919749196491994919849185491844918749186491894918849191491904936949368493714937049373493724937549374493614936049363493624936549364"
output3 ="4936749366493534935249355493544935749356493594935849345493444934749346493494934849351493504940149400494034940249405494044940749265492654926549265492654926549265492654926549265"

a = output1 + output2 + output3

value1 = [a[i:i+5] for i in range(0,len(a),5)]
for i,j in zip(text,value1):
    table[j] =i
print(table)

m = "4934849349493674935749360493664940249346493534935849348493574936549351493644937449348493464936449365493744935349360493464935449364493574935749374493494935349358493594935449404"
mm =[m[i:i+5] for i in range(0,len(m),5)]

for i in mm:
    print(table[i],end='')
# utflag{shouldve_used_haskell_thonk}

Retro Cookie Clicker

gameboy游戏用gbg模拟器运行。通过不断的改变值搜索内存中的值来找到关键内存位置。

这里我们搜索豆子的值,当豆子为3时发现只有CB94处的值为3。这类游戏的变量存储一般就在一块内存内,不会相隔太远,运行游戏改变donze的值,发现CB96处的值改变这里就是donze的值,因为要获取最大值直接输入ff,发现它往CB97的位置进了1位,于是把两边都改成FF FF发现值变成了负数,说明是有符号的,所以最大值应该是0111 1111 1111 1111,转成小端序就是FF 7F修改后就可以获得flag。

image-20250318000931403

flag

image-20250318001901990