0

I am attempting to run a bash command in my python script, but it is failing with:

sh: -c: line 0: syntax error near unexpected token('`

The script is pretty simple...

import os

os.system('bash <(curl -f -L -sS https://ngxpagespeed.com/install) --assume-yes --nginx-version latest -a "--prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/media/cache/nginx/client_temp --http-proxy-temp-path=/media/cache/nginx/proxy_temp --http-fastcgi-temp-path=/media/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/media/cache/nginx/uwsgi_temp --http-scgi-temp-path=/media/cache/nginx/scgi_temp --user=www-data --group=www-data --with-file-aio --with-threads --with-ipv6 --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-http_xslt_module --with-http_image_filter_module --with-stream --with-stream_ssl_module"')

But I cannot see where I am going wrong with it.

The exact command run in shell works fine:

bash <(curl -f -L -sS https://ngxpagespeed.com/install) --assume-yes \
  --nginx-version latest -a "--prefix=/etc/nginx \
  --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules \
  --conf-path=/etc/nginx/nginx.conf \
  --error-log-path=/var/log/nginx/error.log \
  --http-log-path=/var/log/nginx/access.log \
  --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock \
  --http-client-body-temp-path=/media/cache/nginx/client_temp \
  --http-proxy-temp-path=/media/cache/nginx/proxy_temp \
  --http-fastcgi-temp-path=/media/cache/nginx/fastcgi_temp \
  --http-uwsgi-temp-path=/media/cache/nginx/uwsgi_temp \
  --http-scgi-temp-path=/media/cache/nginx/scgi_temp --user=www-data \
  --group=www-data --with-file-aio --with-threads --with-ipv6 \
  --with-http_addition_module --with-http_auth_request_module \
  --with-http_dav_module --with-http_flv_module --with-http_gunzip_module \
  --with-http_gzip_static_module --with-http_mp4_module \
  --with-http_random_index_module --with-http_realip_module \
  --with-http_secure_link_module --with-http_slice_module \
  --with-http_ssl_module --with-http_stub_status_module \
  --with-http_sub_module --with-http_v2_module --with-mail \
  --with-mail_ssl_module --with-http_xslt_module \
  --with-http_image_filter_module --with-stream --with-stream_ssl_module"

Any ideas on how I can fix this?

12
  • 3
    I see you're using a bash-ism <(...), but the error report says sh: -c: line 0: syntax error near unexpected token '('. Note that it says sh, not bash. I'd try to use /bin/bash in the os.system invocation; maybe you get sh substituted for bash in the env where you run the Python code. Commented Jan 10, 2018 at 18:27
  • 1
    I'd try to make the simplest thing work first, e.g. bash <(echo "echo 'It worked'"). I'd also look at subprocess module that might help sidestep the wrong shell issue by not invoking the user's shell at all. Commented Jan 10, 2018 at 18:37
  • 1
    @9000, subprocess doesn't help in and of itself -- using it with shell=True still uses /bin/sh -- but with shell=False, it gives the user full control of which, if any, shell is used. Commented Jan 10, 2018 at 18:39
  • 2
    @9000, ...note that system() doesn't use "the user's" shell -- it's hardcoded to /bin/sh (as well it should be -- that way it's guaranteed to be POSIX-compliant, rather than whatever random shell someone decides they want to use interactively; otherwise, someone using fish or planck would break a whole lot of programs). Commented Jan 10, 2018 at 18:40
  • 1
    @CharlesDuffy: Indeed! os.system does not allow to run without a shell; shell=False, subprocess just calls execvp without invoking a shell. Commented Jan 10, 2018 at 18:42

2 Answers 2

5

/bin/sh, as used by system(), does not support <(). Use bash instead, not just to invoke the downloaded script, but also to interpret the command that runs the download.

script = '''bash <(curl -f -L -sS https://ngxpagespeed.com/install) --assume-yes --nginx-version latest -a "--prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/media/cache/nginx/client_temp --http-proxy-temp-path=/media/cache/nginx/proxy_temp --http-fastcgi-temp-path=/media/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/media/cache/nginx/uwsgi_temp --http-scgi-temp-path=/media/cache/nginx/scgi_temp --user=www-data --group=www-data --with-file-aio --with-threads --with-ipv6 --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-http_xslt_module --with-http_image_filter_module --with-stream --with-stream_ssl_module'''
subprocess.Popen(['bash', '-c', script])

Alternately, the outer script can be rewritten easily enough to be POSIX-compliant, passing the code to run to the interpreter on stdin rather than via a filename or process substitution:

os.system('curl -f -L -sS https://ngxpagespeed.com/install | bash -s --assume-yes  --nginx-version latest -a "--prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/media/cache/nginx/client_temp --http-proxy-temp-path=/media/cache/nginx/proxy_temp --http-fastcgi-temp-path=/media/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/media/cache/nginx/uwsgi_temp --http-scgi-temp-path=/media/cache/nginx/scgi_temp --user=www-data --group=www-data --with-file-aio --with-threads --with-ipv6 --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-http_xslt_module --with-http_image_filter_module --with-stream --with-stream_ssl_module')

By the way -- downloading unsigned code over the Internet and directly invoking it in this manner is a really, really bad idea, and I'm not by any means intending to condone it by answering this question.

Sign up to request clarification or add additional context in comments.

1 Comment

Appreciate the help... i did not know that about system. Seems I have to rethink this further, as the script will not run sudo make install now once the rest of it is finished LOL Thanks Google :D
0

Switching it to this has "fixed" the issue

os.system('wget -O /tmp/install https://ngxpagespeed.com/install')
os.system('bash /tmp/install --assume-yes --nginx-version latest -a "--prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/media/cache/nginx/client_temp --http-proxy-temp-path=/media/cache/nginx/proxy_temp --http-fastcgi-temp-path=/media/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/media/cache/nginx/uwsgi_temp --http-scgi-temp-path=/media/cache/nginx/scgi_temp --user=www-data --group=www-data --with-file-aio --with-threads --with-ipv6 --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-http_xslt_module --with-http_image_filter_module --with-stream --with-stream_ssl_module"')

1 Comment

BTW, if you are going to use a temporary file, use the tempfile.NamedTempFile module, or otherwise create a random, unique name -- it's not remotely unheard for an attacker to precreate symlinks in /tmp to cause other processes to overwrite privileged files that that attacker themselves doesn't have sufficient permissions to write.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.