scufFed

GreyCTF 2025

1661 words | 8 minutes

Update CAA 260625: Currently, I’ve only got the qualifier writeups here. That’ll change after finals.

Reverse Engineering

whr cat

I don’t usually document cheese solves (blatant lie). but this one was especially funny so I’ll jot it down.

Initial Scoping

We’re given three files, runner, chall.sad and catt.enc. Trying to run runner shows us we need to pass chall.sad as a file to run, and finnicking with the arguments more reveals how the encryption made to produce catt.enc is conducted.

Fig: LGTM

So the challenge is clear: figure out the input catt.txt that produces catt.enc. Cool. This is probably some byte VM challenge, so we toss runner into Ghidra to take a looky, and well…

Fig: Yucky

This byte VM is a bit yucky. Here we have two options: solve this the way God intended, or cheese it.

Decompiling Like a Good Kitty

We will be obedient. Let’s break down each function one by one. I’ll be using the default variable names that Ghidra produces, so bear with me. Our byte VM running code starts at 0x001018df, and the important arguments are:

Jumping into the function…

1    FUN_00103298(local_5e88);
2    FUN_0010371a(local_5df8);
3    FUN_0010371a(local_3ea8);
4    FUN_001037b9(local_5df8,local_3ea8);
5    FUN_001037b9(local_3ea8,0xffffffffffffffff);

We push on and define a bunch of the functions. Cleaning up, we get the following:

  1  while (iVar2 = is_empty(stack1), iVar2 == 0) {
  2    opcode = *(byte *)(bytecode + ip);
  3    iVar2 = (int)ip;
  4    if (opcode == 0x99) {
  5      opcode = read_char(bytecode,iVar2 + 1);
  6      local_5e08 = 0;
  7      syscall((long)regs,stack2_,(ulong)opcode);
  8      if (local_5e08 == -1) break;
  9      ip = ip + 2;
 10    }
 11    else if (opcode < 0x9a) {
 12      if (opcode == 0x40) {
 13        ip = *stack2_;
 14        if (ip == -1) break;
 15        stack2_ = (long *)pop(stack1);
 16      }
 17      else if (opcode < 0x41) {
 18        if (opcode == 0x3f) {
 19          jmp_pos = read_int(bytecode,iVar2 + 1);
 20          clear_stack(local_1f58);
 21          push(stack1,local_1f58);
 22          push(local_1f58,ip + 9);
 23          ip = jmp_pos + ip;
 24        }
 25        else if (opcode < 0x40) {
 26          if (opcode == 0x3e) {
 27            jmp_pos = read_int(bytecode,iVar2 + 1);
 28            uVar1 = read_char(bytecode,(int)ip + 9);
 29            uVar4 = get_reg(regs,uVar1);
 30            uVar1 = read_char(bytecode,(int)ip + 10);
 31            uVar5 = get_reg(regs,uVar1);
 32            iVar2 = neq(uVar4,uVar5);
 33            if (iVar2 == 0) {
 34              ip = ip + 0xb;
 35            }
 36            else {
 37              ip = jmp_pos + ip;
 38            }
 39          }
 40          else if (opcode < 0x3f) {
 41            if (opcode == 0x3d) {
 42              jmp_pos = read_int(bytecode,iVar2 + 1);
 43              uVar1 = read_char(bytecode,(int)ip + 9);
 44              uVar4 = get_reg(regs,uVar1);
 45              uVar1 = read_char(bytecode,(int)ip + 10);
 46              uVar5 = get_reg(regs,uVar1);
 47              iVar2 = eq(uVar4,uVar5);
 48              if (iVar2 == 0) {
 49                ip = ip + 0xb;
 50              }
 51              else {
 52                ip = jmp_pos + ip;
 53              }
 54            }
 55            else if (opcode < 0x3e) {
 56              if (opcode == 0x3c) {
 57                jmp_pos = read_int(bytecode,iVar2 + 1);
 58                uVar1 = read_char(bytecode,(int)ip + 9);
 59                uVar4 = get_reg(regs,uVar1);
 60                uVar1 = read_char(bytecode,(int)ip + 10);
 61                uVar5 = get_reg(regs,uVar1);
 62                iVar2 = leq(uVar4,uVar5);
 63                if (iVar2 == 0) {
 64                  ip = ip + 0xb;
 65                }
 66                else {
 67                  ip = jmp_pos + ip;
 68                }
 69              }
 70              else if (opcode < 0x3d) {
 71                if (opcode == 0x3b) {
 72                  jmp_pos = read_int(bytecode,iVar2 + 1);
 73                  uVar1 = read_char(bytecode,(int)ip + 9);
 74                  uVar4 = get_reg(regs,uVar1);
 75                  uVar1 = read_char(bytecode,(int)ip + 10);
 76                  uVar5 = get_reg(regs,uVar1);
 77                  iVar2 = geq(uVar4,uVar5);
 78                  if (iVar2 == 0) {
 79                    ip = ip + 0xb;
 80                  }
 81                  else {
 82                    ip = jmp_pos + ip;
 83                  }
 84                }
 85                else if (opcode < 0x3c) {
 86                  if (opcode == 0x3a) {
 87                    jmp_pos = read_int(bytecode,iVar2 + 1);
 88                    uVar1 = read_char(bytecode,(int)ip + 9);
 89                    uVar4 = get_reg(regs,uVar1);
 90                    uVar1 = read_char(bytecode,(int)ip + 10);
 91                    uVar5 = get_reg(regs,uVar1);
 92                    iVar2 = lt(uVar4,uVar5);
 93                    if (iVar2 == 0) {
 94                      ip = ip + 0xb;
 95                    }
 96                    else {
 97                      ip = jmp_pos + ip;
 98                    }
 99                  }
100                  else if (opcode < 0x3b) {
101                    if (opcode == 0x39) {
102                      jmp_pos = read_int(bytecode,iVar2 + 1);
103                      uVar1 = read_char(bytecode,(int)ip + 9);
104                      uVar4 = get_reg(regs,uVar1);
105                      uVar1 = read_char(bytecode,(int)ip + 10);
106                      uVar5 = get_reg(regs,uVar1);
107                      iVar2 = gt(uVar4,uVar5);
108                      if (iVar2 == 0) {
109                        ip = ip + 0xb;
110                      }
111                      else {
112                        ip = jmp_pos + ip;
113                      }
114                    }
115                    else if (opcode < 0x3a) {
116                      if (opcode == 0x38) {
117                        jmp_pos = read_int(bytecode,iVar2 + 1);
118                        ip = jmp_pos + ip;
119                      }
120                      else if (opcode < 0x39) {
121                        if (opcode == 0x23) {
122                          uVar1 = read_char(bytecode,iVar2 + 1);
123                          uVar4 = get_reg(regs,uVar1);
124                          uVar5 = read_int(bytecode,(int)ip + 2);
125                          movi_(uVar4,uVar5);
126                          ip = ip + 10;
127                        }
128                        else if (opcode < 0x24) {
129                          if (opcode == 0x22) {
130                            uVar1 = read_char(bytecode,iVar2 + 1);
131                            plVar3 = (long *)get_reg(regs,uVar1);
132                            iVar2 = read_short(bytecode,(int)ip + 2);
133                            if (plVar3 == &local_5e08) {
134                              for (j = 0; j < param_3; j = j + 1) {
135                                if (iVar2 == *(int *)(param_1 + (long)j * 4)) {
136                                  local_5e08 = *(long *)(param_2 + (long)j * 8);
137                                }
138                              }
139                            }
140                            else {
141                              movi__(plVar3,iVar2);
142                            }
143                            ip = ip + 6;
144                          }
145                          else if (opcode < 0x23) {
146                            if (opcode == 0x21) {
147                              uVar1 = read_char(bytecode,iVar2 + 1);
148                              uVar4 = get_reg(regs,uVar1);
149                              uVar1 = read_char(bytecode,(int)ip + 2);
150                              movi(uVar4,uVar1);
151                              ip = ip + 3;
152                            }
153                            else if (opcode < 0x22) {
154                              if (opcode == 0x20) {
155                                uVar1 = read_char(bytecode,iVar2 + 1);
156                                uVar4 = get_reg(regs,uVar1);
157                                uVar1 = read_char(bytecode,(int)ip + 2);
158                                uVar5 = get_reg(regs,uVar1);
159                                mov___(uVar4,uVar5);
160                                ip = ip + 3;
161                              }
162                              else if (opcode < 0x21) {
163                                if (opcode == 0x19) {
164                                  uVar1 = read_char(bytecode,iVar2 + 1);
165                                  puVar6 = (undefined8 *)get_reg(regs,uVar1);
166                                  uVar4 = pop_(stack2_);
167                                  *puVar6 = uVar4;
168                                  ip = ip + 2;
169                                }
170                                else if (opcode < 0x1a) {
171                                  if (opcode == 0x18) {
172                                    uVar1 = read_char(bytecode,iVar2 + 1);
173                                    uVar4 = get_reg(regs,uVar1);
174                                    push_(stack2_,uVar4);
175                                    ip = ip + 2;
176                                  }
177                                  else if (opcode < 0x19) {
178                                    if (opcode == 0x17) {
179                                      uVar1 = read_char(bytecode,iVar2 + 1);
180                                      uVar4 = get_reg(regs,uVar1);
181                                      uVar1 = read_char(bytecode,(int)ip + 2);
182                                      uVar5 = get_reg(regs,uVar1);
183                                      xor(uVar4,uVar5);
184                                      ip = ip + 3;
185                                    }
186                                    else if (opcode < 0x18) {
187                                      if (opcode == 0x16) {
188                                        uVar1 = read_char(bytecode,iVar2 + 1);
189                                        uVar4 = get_reg(regs,uVar1);
190                                        uVar1 = read_char(bytecode,(int)ip + 2);
191                                        uVar5 = get_reg(regs,uVar1);
192                                        and(uVar4,uVar5);
193                                        ip = ip + 3;
194                                      }
195                                      else if (opcode < 0x17) {
196                                        if (opcode == 0x15) {
197                                          uVar1 = read_char(bytecode,iVar2 + 1);
198                                          uVar4 = get_reg(regs,uVar1);
199                                          uVar1 = read_char(bytecode,(int)ip + 2);
200                                          uVar5 = get_reg(regs,uVar1);
201                                          or(uVar4,uVar5);
202                                          ip = ip + 3;
203                                        }
204                                        else if (opcode < 0x16) {
205                                          if (opcode == 0x14) {
206                                            uVar1 = read_char(bytecode,iVar2 + 1);
207                                            uVar4 = get_reg(regs,uVar1);
208                                            uVar1 = read_char(bytecode,(int)ip + 2);
209                                            uVar5 = get_reg(regs,uVar1);
210                                            div(uVar4,uVar5);
211                                            ip = ip + 3;
212                                          }
213                                          else if (opcode < 0x15) {
214                                            if (opcode == 0x13) {
215                                              uVar1 = read_char(bytecode,iVar2 + 1);
216                                              uVar4 = get_reg(regs,uVar1);
217                                              uVar1 = read_char(bytecode,(int)ip + 2);
218                                              uVar5 = get_reg(regs,uVar1);
219                                              local_5e78 = divmod(uVar4,uVar5);
220                                              ip = ip + 3;
221                                            }
222                                            else if (opcode < 0x14) {
223                                              if (opcode == 0x12) {
224                                                uVar1 = read_char(bytecode,iVar2 + 1);
225                                                uVar4 = get_reg(regs,uVar1);
226                                                uVar1 = read_char(bytecode,(int)ip + 2);
227                                                uVar5 = get_reg(regs,uVar1);
228                                                mul(uVar4,uVar5);
229                                                ip = ip + 3;
230                                              }
231                                              else if (opcode < 0x13) {
232                                                if (opcode == 0x10) {
233                                                  uVar1 = read_char(bytecode,iVar2 + 1);
234                                                  uVar4 = get_reg(regs,uVar1);
235                                                  uVar1 = read_char(bytecode,(int)ip + 2);
236                                                  uVar5 = get_reg(regs,uVar1);
237                                                  add(uVar4,uVar5);
238                                                  ip = ip + 3;
239                                                }
240                                                else if (opcode == 0x11) {
241                                                  uVar1 = read_char(bytecode,iVar2 + 1);
242                                                  uVar4 = get_reg(regs,uVar1);
243                                                  uVar1 = read_char(bytecode,(int)ip + 2);
244                                                  uVar5 = get_reg(regs,uVar1);
245                                                  sub(uVar4,uVar5);
246                                                  ip = ip + 3;
247                                                }
248                                              }
249                                            }
250                                          }
251                                        }
252                                      }
253                                    }
254                                  }
255                                }
256                              }
257                            }
258                          }
259                        }
260                      }
261                    }
262                  }
263                }
264              }
265            }
266          }
267        }
268      }
269    }
270  }

The most interesting of the bunch is opcode 0x22 as there’s sort of a lookup table bit. Also, get_reg is a bit of a misnomer as it could also be getting the value as opposed to the register pointer. Anyways, we now have enough to press on and get some LLM to make a decompiler for us:

The Way of the Cheese

meowware

meowware is a tricky “fileless” malware challenge which required some hoop jumping to solve. During the course of the qualifiers, I decided to focus my efforts on other challenges (cough, blockchain, cough), but this was a challenge I felt would be good to upsolve.

We’re given a client file and a PCAP file. Unfortunately, the PCAP doesn’t give us much to work with as the communication is encrypted:

Fig: Looks encrypted to me