얼마 전에 curl
커맨드 라인 인수로 지정된 사용자 이름과 비밀번호 가 ps
출력에 나타나지 않는다는 것을 알았습니다 (물론 bash 기록에 나타날 수도 있습니다).
마찬가지로에 표시되지 않습니다 /proc/PID/cmdline
.
(그러나 결합 된 username / password 인수의 길이는 파생 될 수 있습니다.)
아래 데모 :
[root@localhost ~]# nc -l 80 &
[1] 3342
[root@localhost ~]# curl -u iamsam:samiam localhost &
[2] 3343
[root@localhost ~]# GET / HTTP/1.1
Authorization: Basic aWFtc2FtOnNhbWlhbQ==
User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.15.3 zlib/1.2.3 libidn/1.18 libssh2/1.4.2
Host: localhost
Accept: */*
[1]+ Stopped nc -l 80
[root@localhost ~]# jobs
[1]+ Stopped nc -l 80
[2]- Running curl -u iamsam:samiam localhost &
[root@localhost ~]# ps -ef | grep curl
root 3343 3258 0 22:37 pts/1 00:00:00 curl -u localhost
root 3347 3258 0 22:38 pts/1 00:00:00 grep curl
[root@localhost ~]# od -xa /proc/3343/cmdline
0000000 7563 6c72 2d00 0075 2020 2020 2020 2020
c u r l nul - u nul sp sp sp sp sp sp sp sp
0000020 2020 2020 0020 6f6c 6163 686c 736f 0074
sp sp sp sp sp nul l o c a l h o s t nul
0000040
[root@localhost ~]#
이 효과는 어떻게 달성됩니까? 의 소스 코드 어딘가에 curl
있습니까? ( curl
기능 이 아닌 기능 이라고 가정 ps
합니까? 아니면 일종의 커널 기능입니까?)
또한 : 바이너리 실행 파일의 소스 코드 외부 에서이 작업을 수행 할 수 있습니까? 예를 들어 아마도 루트 권한과 결합 된 쉘 명령을 사용합니까?
즉 , 임의의 쉘 명령에 전달 된 인수 /proc
가 ps
출력에 나타나 거나 출력 되는 것을 막을 수 있습니까 (동일한 것 같습니다) ? (나는 이것에 대한 대답이 “아니오”라고 생각하지만이 여분의 반 질문을 포함시키는 것이 가치가있는 것 같습니다.)
답변
커널은 프로세스를 실행할 때 프로세스에 속하는 읽기 쓰기 메모리 (적어도 Linux에서는)에 명령 행 인수를 복사합니다. 프로세스는 다른 메모리와 마찬가지로 해당 메모리에 쓸 수 있습니다. 때 ps
인수를 표시, 그것은 프로세스의 메모리의 특정 주소에 저장되어있는 어떤 다시 읽습니다. 대부분의 프로그램은 원래 주장을 유지하지만 변경할 수 있습니다. 상태 의 POSIX 설명은ps
표시된 문자열이 시작될 때 명령에 전달 된 인수 목록의 버전인지 또는 응용 프로그램에 의해 수정되었을 수있는 인수의 버전인지 여부는 지정되지 않습니다. 응용 프로그램은 인수 목록을 수정할 수 있고 ps의 출력에 해당 수정 사항이 반영되도록 할 수 없습니다.
이것이 언급되는 이유는 대부분의 유닉스 변형이 변경 사항을 반영하지만 다른 유형의 운영 체제에서의 POSIX 구현은 그렇지 않을 수 있기 때문입니다.
이 기능은 프로세스가 임의로 변경할 수 없으므로 제한적으로 사용됩니다. 프로그램 ps
이 인수를 가져올 위치를 변경할 수없고 영역을 원래 크기 이상으로 확장 할 수 없기 때문에 최소한 인수의 총 길이를 늘릴 수 없습니다. 인수는 C 스타일의 널 종료 문자열이기 때문에 길이가 널 바이트를 사용함으로써 효과적으로 길이를 줄일 수 있습니다 (이것은 끝에 빈 인수가없는 것과 구별 할 수 없습니다).
정말로 파고 싶다면 오픈 소스 구현의 소스를 볼 수 있습니다. 리눅스의 소스는 ps
당신이 보는 것 모두가에서 명령 행 인수 읽는이, 흥미없는 proc 파일 시스템을 에 . 이 파일의 내용을 생성하는 코드는의 커널 에 있습니다. 프로세스 메모리의 일부 (로 액세스 )는 주소 에서 ; 이 주소는 프로세스가 시작될 때 커널에 기록되며 나중에 변경할 수 없습니다./proc/PID/cmdline
proc_pid_cmdline_read
fs/proc/base.c
access_remote_vm
mm->arg_start
mm->arg_end
어떤 데몬이 자신의 상태를 반영하기 위해이 기능을 사용하여, 예를 들어, 그들은 그들의 변화 argv[1]
유사한 문자열로 starting
또는 available
나 exiting
. 많은 유닉스 변형은 setproctitle
이것을 수행 하는 기능을 가지고 있습니다 . 일부 프로그램은이 기능을 사용하여 기밀 데이터를 숨 깁니다. 프로세스가 시작되는 동안 명령 행 인수가 표시되므로 이는 제한적으로 사용됩니다.
대부분의 고급 언어는 인수를 문자열 객체에 복사하고 원래 저장소를 수정할 수있는 방법을 제공하지 않습니다. 다음은 argv
요소를 직접 변경하여이 기능을 보여주는 C 프로그램입니다 .
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
int i;
system("ps -p $PPID -o args=");
for (i = 0; i < argc; i++)
{
memset(argv[i], '0' + (i % 10), strlen(argv[i]));
}
system("ps -p $PPID -o args=");
return 0;
}
샘플 출력 :
./a.out hello world
0000000 11111 22222
argv
curl 소스 코드에서 수정 된 것을 볼 수 있습니다 . 컬은를 사용하여 인수를 모든 공백으로 변경하는 데 사용되는 함수 cleanarg
를src/tool_paramhlp.c
정의합니다 memset
. src/tool_getparam.c
이 기능 에서는 사용자 비밀번호 를 수정하는 등 몇 번 사용됩니다 . 함수는 매개 변수 구문 분석에서 호출되므로 컬 호출 초기에 발생하지만이 전에 명령 행을 덤프하면 여전히 비밀번호가 표시됩니다.
인수는 프로세스의 자체 메모리에 저장되므로 디버거를 사용하지 않는 한 외부에서 인수를 변경할 수 없습니다.
답변
다른 답변은 일반적인 방식으로 질문에 잘 대답합니다. ” 이 효과는 어떻게 달성됩니까 ? curl의 소스 코드 어딘가에 있습니까? “
curl 소스 코드 의 인수 구문 분석 섹션 에서 -u
옵션은 다음과 같이 처리됩니다.
case 'u':
/* user:password */
GetStr(&config->userpwd, nextarg);
cleanarg(nextarg);
break;
그리고 cleanarg()
기능은 다음 과 같이 정의 됩니다.
void cleanarg(char *str)
{
#ifdef HAVE_WRITABLE_ARGV
/* now that GetStr has copied the contents of nextarg, wipe the next
* argument out so that the username:password isn't displayed in the
* system process list */
if(str) {
size_t len = strlen(str);
memset(str, ' ', len);
}
#else
(void)str;
#endif
}
따라서 argv
다른 답변에서 설명한 것처럼 username : password 인수의 공백을 공백으로 덮어 쓴 것을 분명히 알 수 있습니다 .
답변
프로세스는 매개 변수를 읽을뿐만 아니라 쓸 수도 있습니다.
나는 개발자가 아니므 로이 물건에 익숙하지 않지만 환경 매개 변수 변경과 비슷한 접근 방식으로 외부에서 가능할 수 있습니다.