proxychains-ng icon indicating copy to clipboard operation
proxychains-ng copied to clipboard

php curl module ignores proxychains

Open kai-rn opened this issue 12 years ago • 14 comments
trafficstars

try this simple script:

<?php
echo "file_get_contents:\n";
echo file_get_contents("http://ifconfig.me/ip");
echo "curl:\n";
$ch=curl_init();
curl_setopt($ch,CURLOPT_URL,"http://ifconfig.me/ip");
curl_setopt($ch,CURLOPT_HEADER,0);
curl_exec($ch);
curl_close($ch);

assume that my real ip is 123.45.67.89 and socks proxy ip is 10.10.10.10 without proxychains:

# php php_curl.php 
file_get_contents:
123.45.67.89
curl:
123.45.67.89

with proxychains:

# proxychains php php_curl.php 
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/libproxychains4.so
file_get_contents:
10.10.10.10
curl:
123.45.67.89

i.e. curl was not proxified even with proxychains library preloaded.

kai-rn avatar Jan 23 '13 22:01 kai-rn

programs that launch other programs are tricky to deal with. maybe environment variables get cleared. try using export LD_PRELOAD=/path/to/proxychains4.so in the shell previous to running your php script. also proxychains works only with dynamically loaded stuff. maybe php is statically linked against curl ? try running gdb --args proxychains4 php myscript.php and put a breakpoint on "connect", and then look at the backtrace whenever the bp is hit.

rofl0r avatar Jan 24 '13 00:01 rofl0r

i did some tests and the following happens: for the php connect itself, libc.so is used in the ordinary way, everything works as expected. for cul, php dlopen()'s curl.so, which opens libcurl.so. for some reason the glibc dynlinker does not hook the connect() symbol for libcurl.so this seems to be a bug in glibc's dynlinker, as it works as expected with musl libc dynlinker. in other words, running your php script using musl libc as system libc, 10.10.10.10 is returned for both calls.

rofl0r avatar Jan 24 '13 01:01 rofl0r

can i use musl libc instead of gnu libc in centos 6? i've tried LD_PRELOAD but it has not fixed the issue.

kai-rn avatar Jan 24 '13 02:01 kai-rn

yeah, you can use musl libc as a secondary libc, but you need to compile everything involved from source using the provided musl-gcc wrapper, that means zlib, openssl, php, curl, and proxychains (everything called must be using the same libc)

here are build recipes for musl (configure flags etc) https://github.com/rofl0r/sabotage/blob/master/pkg/curl https://github.com/rofl0r/sabotage/blob/master/pkg/php https://github.com/rofl0r/sabotage/blob/master/pkg/zlib https://github.com/rofl0r/sabotage/blob/master/pkg/openssl

rofl0r avatar Jan 24 '13 03:01 rofl0r

many thanks for your effort but i'm afraid i wont be able to install all those packages. could you please suggest some simplier workaround?

kai-rn avatar Jan 24 '13 12:01 kai-rn

the simplest thing in your situation is probably to use the built-in proxy support of curl i.e. adapt your php scripts so that they all use the same proxy than proxychains (maybe it even works with an environment variable)

alternatively you could either scratch php and use C, or download a precompiled sabotage linux rootfs from http://mirror.wzff.de/sabotage/ and extract it into some directory (read the http://mirror.wzff.de/sabotage/README_sabotage_images.txt for how to extract the image files) then chroot into that dir and use the php scripts from inside the chroot with musl.

rofl0r avatar Jan 24 '13 14:01 rofl0r

I'm having the same issue on Debian, but in OSX that issue doesn't happen. Any thoughts ?

speeddragon avatar Jan 13 '17 18:01 speeddragon

it's a bug in GLIBCs dynlinker. someone would need to stand up and report the issue on their bugtracker. this comment explains what happens.

rofl0r avatar Jan 13 '17 20:01 rofl0r

i've created a minimal testcase to reproduce the issue: https://0x0.st/fdc.gz it's 3 small C files and a build.sh. when you run build.sh, you should see the output

hooked
hooked

however on GLIBC it probably prints hooked only once. can anyone test this ? i don't have a system with GLIBC. when we have it confirmed, someone can take the testcase and open a bug report on the GLIBC bugtracker.

app.c

#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>

typedef void (*func)(void);

int main() {
        puts("hello world");
        void *h = dlopen("./dlopened.so", RTLD_NOW);
        func doit = dlsym(h, "doit");
        doit();
}

build.sh

#!/bin/sh
gcc preload.c -Wall -shared -fPIC -ldl -lpthread -o preload.so
gcc dlopened.c -Wall -shared -fPIC -ldl -o dlopened.so
gcc app.c -Wall -ldl -o app

LD_PRELOAD=./preload.so ./app
# should print hooked twice.
# if it doesn't, dynlinker is broken

dlopened.c

#include <stdio.h>
void doit(void) {
        puts("puts from dlopened .so");
}

preload.c

#define _GNU_SOURCE
#include <pthread.h>
#include <dlfcn.h>
pthread_once_t init_once = PTHREAD_ONCE_INIT;

typedef int (*putsfunc)(const char*);
putsfunc origputs;

static void init(void) {
        origputs = dlsym(RTLD_NEXT, "puts");
}
__attribute__((constructor))
static void gcc_init(void) {
        pthread_once(&init_once, init);
}
int puts(const char* s) {
        origputs("hooked");
        return 1;
}

rofl0r avatar Jan 15 '17 00:01 rofl0r

On 2017-01-15 07:01, rofl0r wrote:

i've created a minimal testcase to reproduce the issue: https://0x0.st/fDK.gz it's 3 small C files and a build.sh. when you run build.sh, you should see the output

hooked
hooked

however on GLIBC it probably prints hooked only once. can anyone test this ? i don't have a system with GLIBC. when we have it confirmed, someone can take the testcase and open a bug report on the GLIBC bugtracker.

problem:

 dlopen_bug> gcc preload.c -Wall -shared -fPIC -ldl -o preload.so
 preload.c: In function ‘init’:
 preload.c:9:19: error: ‘RTLD_NEXT’ undeclared (first use in this 

function) origputs = dlsym(RTLD_NEXT, "puts"); ^

fix:

 gcc preload.c -D_GNU_SOURCE -Wall -shared -fPIC -ldl -o preload.so

or add a line:

 --- preload.c   2017-01-15 14:13:06.951753115 +0700
 +++ preload_fix.c       2017-01-15 14:13:03.459753218 +0700
 @@ -1,3 +1,4 @@
 +#define _GNU_SOURCE
  #include <pthread.h>
  #include <dlfcn.h>
  pthread_once_t init_once = PTHREAD_ONCE_INIT;

problem:

 dlopen_bug> sh build.sh
 app.c: In function ‘main’:
 app.c:11:1: warning: control reaches end of non-void function 

[-Wreturn-type] } ^ ./app: symbol lookup error: ./preload.so: undefined symbol: pthread_once

fix:

 gcc preload.c -D_GNU_SOURCE -lpthread -Wall -shared -fPIC -ldl -o 

preload.so

But in the end it surprisingly works on Centos 6.8 gcc 4.4.7 glibc 2.12 (system of the test case with php and curl) and openSUSE 13.2 gcc 4.8.3 glibc 2.19:

 dlopen_bug> sh build.sh
 app.c: In function ‘main’:
 app.c:11:1: warning: control reaches end of non-void function 

[-Wreturn-type] } ^ hooked hooked

kai-rn avatar Jan 15 '17 07:01 kai-rn

thanks. what was the output of the testcase once you got it to run, @kai-rn ?

rofl0r avatar Jan 15 '17 10:01 rofl0r

Github broke my email formatting, press on "..." icon to see the rest of the email. The output is as follows:

But in the end it surprisingly works on Centos 6.8 gcc 4.4.7 glibc 2.12 (system of the test case with php and curl) and openSUSE 13.2 gcc 4.8.3 glibc 2.19:

dlopen_bug> sh build.sh app.c: In function ‘main’: app.c:11:1: warning: control reaches end of non-void function [-Wreturn-type] } ^ hooked hooked

kai-rn avatar Jan 15 '17 11:01 kai-rn

damn. even when you replace RTLD_NOW with RTLD_LAZY in app.c ?

rofl0r avatar Jan 15 '17 12:01 rofl0r

On 2017-01-15 19:28, rofl0r wrote:

damn. even when you replace RTLD_NOW with RTLD_LAZY in app.c ?

still works.

hooked hooked

kai-rn avatar Jan 15 '17 13:01 kai-rn