Just get the flag dummy.
To connect, spawn an instance then run
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ProxyCommand='openssl s_client -connect <host>:1 -quiet' ctf@localhost
with password ctf.
Connecting to the server, we can find the folders with the same date of Aug 29
as /home/ctf
:
$ ls -al /
total 60
drwxr-xr-x 1 root root 4096 Aug 31 13:59 .
drwxr-xr-x 1 root root 4096 Aug 31 13:59 ..
lrwxrwxrwx 1 root root 7 Aug 11 00:00 bin -> usr/bin
drwxr-xr-x 2 root root 4096 May 9 14:50 boot
drwxr-xr-x 5 root root 360 Aug 31 13:59 dev
drwxr-xr-x 1 root root 4096 Aug 31 13:59 etc
drwxr-xr-x 1 root root 4096 Aug 29 16:18 home
lrwxrwxrwx 1 root root 7 Aug 11 00:00 lib -> usr/lib
lrwxrwxrwx 1 root root 9 Aug 11 00:00 lib64 -> usr/lib64
drwxr-xr-x 2 root root 4096 Aug 11 00:00 media
drwxr-xr-x 2 root root 4096 Aug 11 00:00 mnt
drwxr-xr-x 1 root root 4096 Aug 29 16:18 opt
dr-xr-xr-x 379 root root 0 Aug 31 13:59 proc
drwx------ 2 root root 4096 Aug 11 00:00 root
drwxr-xr-x 1 root root 4096 Aug 31 14:05 run
lrwxrwxrwx 1 root root 8 Aug 11 00:00 sbin -> usr/sbin
drwxr-xr-x 2 root root 4096 Aug 11 00:00 srv
dr-xr-xr-x 13 root root 0 Aug 31 02:47 sys
drwxrwxrwt 2 root root 4096 Aug 11 00:00 tmp
drwxr-xr-x 1 root root 4096 Aug 11 00:00 usr
drwxr-xr-x 1 root root 4096 Aug 11 00:00 var
So there must be something in /opt
:
$ find /opt
/opt
/opt/ctf
/opt/ctf/flag.blob
$ base64 /opt/ctf/flag*
KiIppwcOdIzGO5FtC1CEHFsVodUEi4nPMdpd54Kc8ZB1TLqc6r8kJRlyGr0E6YhhCkbDVA==
Now we get the base64 of flag.blob
, but it is some unknown binary. Look for blob
in the filesystem:
$ cd /usr
$ grep -R blob
grep: lib/x86_64-linux-gnu/libsystemd.so.0.35.0: binary file matches
grep: lib/x86_64-linux-gnu/libgpg-error.so.0.33.1: binary file matches
grep: lib/x86_64-linux-gnu/libgpg-error.so.0: binary file matches
grep: lib/x86_64-linux-gnu/libsystemd.so.0: binary file matches
grep: lib/x86_64-linux-gnu/libselinux.so.1: binary file matches
lib/x86_64-linux-gnu/perl-base/Config.pm:# https://github.com/Perl/perl5/blob/blead/Porting/Glossary
grep: lib/x86_64-linux-gnu/libfido2.so.1.12.0: binary file matches
grep: lib/x86_64-linux-gnu/libfido2.so.1: binary file matches
grep: lib/x86_64-linux-gnu/libcrypto.so.3: binary file matches
grep: lib/x86_64-linux-gnu/engines-3/loader_attic.so: binary file matches
grep: lib/x86_64-linux-gnu/libnss_ctf.so.2: binary file matches
grep: lib/openssh/ssh-sk-helper: binary file matches
grep: lib/openssh/ssh-keysign: binary file matches
grep: sbin/sshd: binary file matches
grep: bin/gpgv: binary file matches
grep: bin/ssh-keygen: binary file matches
grep: bin/slogin: binary file matches
grep: bin/ssh: binary file matches
grep: bin/ssh-add: binary file matches
The lib/x86_64-linux-gnu/libnss_ctf.so.2
file is worth looking. Grab it locally via base64 lib/x86_64-linux-gnu/libnss_ctf.so.2
.
Decompile via Ghidra:
undefined8 _nss_ctf_getpwnam_r(char *param_1)
{
int iVar1;
char *__s2;
undefined4 *in_R8;
__s2 = (char *)get_trigger_user();
iVar1 = strcmp(param_1,__s2);
if (iVar1 == 0) {
FUN_00101327();
}
*in_R8 = 2;
return 0;
}
undefined * get_trigger_user(void)
{
byte local_15 [9];
uint local_c;
local_15[0] = 0x11;
local_15[1] = 0x16;
local_15[2] = 0x11;
local_15[3] = 0xb;
local_15[4] = 0x1a;
local_15[5] = 0x11;
local_15[6] = 0x1b;
local_15[7] = 0x10;
local_15[8] = 0x7f;
for (local_c = 0; local_c < 9; local_c = local_c + 1) {
(&DAT_001040b8)[(int)local_c] = local_15[(int)local_c] ^ 0x7f;
}
return &DAT_001040b8;
}
void FUN_00101327(void)
{
byte bVar1;
char *pcVar2;
undefined4 local_34;
FILE *local_30;
char *content;
size_t local_20;
FILE *local_18;
long local_10;
if (DAT_001040c4 == 0) {
pcVar2 = (char *)get_blob_path();
local_18 = fopen(pcVar2,"rb");
if (local_18 != (FILE *)0x0) {
fseek(local_18,0,2);
local_20 = ftell(local_18);
rewind(local_18);
content = (char *)malloc(local_20 + 1);
if (content == (char *)0x0) {
fclose(local_18);
}
else {
fread(content,1,local_20,local_18);
fclose(local_18);
local_34 = 0x3244ad92;
for (local_10 = 0; local_10 < (long)local_20; local_10 = local_10 + 1) {
bVar1 = FUN_001012ea(&local_34);
content[local_10] = content[local_10] ^ bVar1;
}
content[local_20] = '\0';
pcVar2 = (char *)get_outfile();
local_30 = fopen(pcVar2,"w");
if (local_30 != (FILE *)0x0) {
fputs(content,local_30);
fclose(local_30);
}
memset(content,0,local_20);
free(content);
DAT_001040c4 = 1;
}
}
}
return;
}
undefined * get_blob_path(void)
{
byte local_28 [28];
uint local_c;
local_28[0] = 0x85;
local_28[1] = 0xc5;
local_28[2] = 0xda;
local_28[3] = 0xde;
local_28[4] = 0x85;
local_28[5] = 0xc9;
local_28[6] = 0xde;
local_28[7] = 0xcc;
local_28[8] = 0x85;
local_28[9] = 0xcc;
local_28[10] = 0xc6;
local_28[0xb] = 0xcb;
local_28[0xc] = 0xcd;
local_28[0xd] = 0x84;
local_28[0xe] = 200;
local_28[0xf] = 0xc6;
local_28[0x10] = 0xc5;
local_28[0x11] = 200;
local_28[0x12] = 0xaa;
for (local_c = 0; local_c < 0x13; local_c = local_c + 1) {
(&DAT_00104090)[(int)local_c] = local_28[(int)local_c] ^ 0xaa;
}
return &DAT_00104090;
}
If we implement get_trigger_user
locally, we can see that the user name is nintendo
:
$ cat get_trigger_user.c
#include <stdint.h>
#include <stdio.h>
int main() {
uint8_t local_15[9];
uint8_t temp[10];
int local_c;
local_15[0] = 0x11;
local_15[1] = 0x16;
local_15[2] = 0x11;
local_15[3] = 0xb;
local_15[4] = 0x1a;
local_15[5] = 0x11;
local_15[6] = 0x1b;
local_15[7] = 0x10;
local_15[8] = 0x7f;
for (local_c = 0; local_c < 9; local_c = local_c + 1) {
temp[(int)local_c] = local_15[(int)local_c] ^ 0x7f;
}
temp[9] = 0;
printf("%s\n", temp);
return 0;
}
$ gcc get_trigger_user.c -o get_trigger_user
$ ./get_trigger_user
nintendo
But we cannot su
in the remote machine. Do the same thing for get_blob_path
:
$ cat get_blob_path.c
#include <stdint.h>
#include <stdio.h>
int main() {
uint8_t local_28[28];
uint8_t temp[29];
int local_c;
local_28[0] = 0x85;
local_28[1] = 0xc5;
local_28[2] = 0xda;
local_28[3] = 0xde;
local_28[4] = 0x85;
local_28[5] = 0xc9;
local_28[6] = 0xde;
local_28[7] = 0xcc;
local_28[8] = 0x85;
local_28[9] = 0xcc;
local_28[10] = 0xc6;
local_28[0xb] = 0xcb;
local_28[0xc] = 0xcd;
local_28[0xd] = 0x84;
local_28[0xe] = 200;
local_28[0xf] = 0xc6;
local_28[0x10] = 0xc5;
local_28[0x11] = 200;
local_28[0x12] = 0xaa;
for (local_c = 0; local_c < 0x13; local_c = local_c + 1) {
temp[(int)local_c] = local_28[(int)local_c] ^ 0xaa;
}
temp[19] = 0;
printf("%s\n", temp);
return 0;
}
$ gcc get_blob_path.c -o get_blob_path
$ ./get_blob_path
/opt/ctf/flag.blob
So we know that the following part is for decoding the /opt/ctf/flag.blob
, which is already known. Just repeat what the libnss_ctf.so
does:
#include <stdint.h>
#include <stdio.h>
uint32_t FUN_001012ea(uint32_t *param_1)
{
uint32_t uVar1;
uVar1 = *param_1 ^ *param_1 << 0xd;
uVar1 = uVar1 ^ uVar1 >> 0x11;
*param_1 = uVar1 ^ uVar1 << 5;
return *param_1;
}
int main() {
char content[100];
FILE *fp = fopen("input", "rb");
fread(content, 1, 52, fp);
int local_20 = 52;
uint32_t local_34 = 0x3244ad92;
uint8_t bVar1;
for (int local_10 = 0; local_10 < (long)local_20; local_10 = local_10 + 1) {
bVar1 = FUN_001012ea(&local_34);
content[local_10] = content[local_10] ^ bVar1;
}
content[local_20] = '\0';
printf("%s\n", content);
}
Get flag: corctf{nsswitch_can_be_sneaky_sometimes_i_guess_idk}
.
P.S. maybe we can just ssh to nintendo@
and grab the flag from the output path.