যখন একটি প্রক্রিয়া একটি কমান্ড কার্যকর করে ( execve()
সিস্টেম কলের মাধ্যমে ), এর স্মৃতি মুছে ফেলা হয়। মৃত্যুদন্ড জুড়ে কিছু তথ্য প্রেরণ করার জন্য, execve()
: সিস্টেম কল যে জন্য দুটি আর্গুমেন্ট লাগে argv[]
এবং envp[]
অ্যারে।
এগুলি দুটি স্ট্রিংয়ের অ্যারে:
argv[]
যুক্তি রয়েছে
envp[]
var=value
বিন্যাসে (কনভেনশন অনুসারে) স্ট্রিং হিসাবে পরিবেশের পরিবর্তনশীল সংজ্ঞা রয়েছে ।
যখন তুমি কর:
export SECRET=value; cmd "$SECRET"
(এখানে প্যারামিটার সম্প্রসারণের আশেপাশে নিখোঁজ উক্তিগুলি জুড়েছে)।
আপনি নির্বাহ করছি cmd
গোপন সঙ্গে ( value
) উভয় পাশ argv[]
এবং envp[]
। argv[]
হবে ["cmd", "value"]
এবং envp[]
কিছু [..., "PATH=/bin:...", "HOME=...", ..., "SECRET=value", "TERM=xterm", ...]
। যেমন পরিবেশের পরিবর্তনশীল থেকে গোপনের মান পুনরুদ্ধার করার জন্য cmd
কোনও getenv("SECRET")
বা সমমানের কাজ করা হয় না , SECRET
পরিবেশে রাখাই কার্যকর নয়।
argv[]
জনসাধারণের জ্ঞান। এটি আউটপুট দেখায় ps
। envp[]
আজকাল হয় না। লিনাক্স এ এটি দেখায় /proc/pid/environ
। এটি ps ewww
বিএসডি-তে (এবং ps
লিনাক্সে প্রোপস-এনজি এর সাথে) আউটপুট দেখায় , তবে কেবল একই কার্যকর ইউআইডি (এবং সেটুয়েড / সেটগিড এক্সিকিউটেবলের জন্য আরও বিধিনিষেধের সাথে) চলমান প্রক্রিয়াগুলিতে। এটি কিছু নিরীক্ষার লগগুলিতে প্রদর্শিত হতে পারে তবে audit নিরীক্ষণ লগগুলি কেবল প্রশাসকদের দ্বারা অ্যাক্সেসযোগ্য হওয়া উচিত।
সংক্ষেপে একটি নির্বাহযোগ্য পরিবেশে প্রবাহিত পরিবেশটি বোঝানো হয় কোনও প্রক্রিয়াটির অভ্যন্তরীণ স্মৃতি হিসাবে ব্যক্তিগত বা কমপক্ষে ব্যক্তিগত হিসাবে (যা কিছু পরিস্থিতিতে সঠিক সুযোগ-সুবিধাগুলি সহ অন্য একটি প্রক্রিয়াও ডিবাগারের সাথে উদাহরণস্বরূপ অ্যাক্সেস করতে পারে এবং পারে এছাড়াও ডিস্কে ফেলে দেওয়া হবে)।
যেহেতু argv[]
জনসাধারণের জ্ঞান, তাই একটি কমান্ড যা প্রত্যাশা করে যে এর কমান্ড লাইনে ডেটাটি গোপনীয় ছিল তা ডিজাইনের মাধ্যমে নষ্ট হয়ে গেছে।
সাধারণত, যে কমান্ডগুলিকে একটি গোপনীয়তা দেওয়া দরকার, আপনাকে পরিবেশের পরিবর্তনশীলের মতো এটি করার জন্য আপনাকে অন্য একটি ইন্টারফেস সরবরাহ করে। এই ক্ষেত্রে:
IPMI_PASSWORD=secret ipmitool -I lan -U admin...
অথবা স্ট্যান্ডিনের মতো ডেডিকেটেড ফাইল বর্ণনাকারীর মাধ্যমে:
echo secret | openssl rsa -passin stdin ...
( echo
অন্তর্নির্মিত হওয়ায় এটি এর ফলাফলের মধ্যে প্রদর্শিত হয় না ps
)
অথবা একটি ফাইল যেমন এর .netrc
জন্য ftp
এবং কয়েকটি অন্যান্য কমান্ড বা
mysql --defaults-extra-file=/some/file/with/password ....
কিছু অ্যাপ্লিকেশন curl
(এবং এটি এখানে @ মিউহও গ্রহণ করেছেন ) প্রাইভিংargv[]
চোখ থেকে প্রাপ্ত পাসওয়ার্ডটি গোপন করার চেষ্টা করে (কিছু সিস্টেমে argv[]
স্ট্রিংগুলি স্ট্রোম থাকা মেমরির অংশটি ওভাররাইট করে )। তবে এটি সত্যই সহায়তা করে না এবং সুরক্ষার একটি মিথ্যা প্রতিশ্রুতি দেয়। execve()
এটি ওভাররাইটিংয়ের মধ্যে একটি উইন্ডো ছেড়ে দেয় যেখানে ps
এখনও গোপনীয়তা দেখাবে।
উদাহরণস্বরূপ, কোনও আক্রমণকারী যদি জানেন যে আপনি কোনও স্ক্রিপ্ট চালাচ্ছেন curl -u user:somesecret https://...
(উদাহরণস্বরূপ ক্রোন জব করে), তাকে যা করতে হবে তা (অনেক) লাইব্রেরি ক্যাশে থেকে উচ্ছেদ করা হবে curl
(উদাহরণস্বরূপ একটি চালিয়ে sh -c 'a=a;while :; do a=$a$a;done'
) এটির প্রারম্ভিক গতি কমিয়ে দেওয়ার জন্য এবং এমনকি until grep 'curl.*[-]u' /proc/*/cmdline; do :; done
আমার পরীক্ষাগুলিতে সেই পাসওয়ার্ডটি ধরতে খুব অদক্ষ করে নেওয়াও যথেষ্ট।
যদি আর্গুমেন্টগুলি কেবলমাত্র আপনি কমান্ডগুলিতে গোপনীয়করণ করতে পারেন তবে এখনও কিছু জিনিস আপনি চেষ্টা করতে পারেন।
লিনাক্সের পুরানো সংস্করণ সহ কয়েকটি সিস্টেমে স্ট্রিংগুলির প্রথম কয়েকটি বাইট (লিনাক্স ৪.১ এবং এর আগে ৪০৯6) argv[]
অনুসন্ধান করা যেতে পারে।
সেখানে, আপনি করতে পারেন:
(exec -a "$(printf %-4096s cmd)" cmd "$secret")
এবং গোপনীয়তা লুকানো হবে কারণ এটি প্রথম 4096 বাইট পেরিয়ে গেছে। ৪.২ যেহেতু লিনাক্স এখন আর এই পদ্ধতিটি ব্যবহার করেছে তাদের অবশ্যই এখন আফসোস করতে হবে কারণ আরগের তালিকাটি আর ছাঁটাই করে না /proc/pid/cmdline
। আরও মনে রাখবেন যে এটি ps
কোনও কমান্ড লাইনের অনেকগুলি বাইট (ফ্রিবিএসডি-তে যেখানে এটি 2048 এর মধ্যে সীমাবদ্ধ বলে মনে হয়) এর চেয়ে বেশি দেখাবে না কারণ এটি একই API এ ps
ব্যবহার করতে পারে না আরও পেতে can't সিস্টেমগুলি যেখানে ps
নিয়মিত ব্যবহারকারীর সেই তথ্য পুনরুদ্ধার করার একমাত্র উপায় যেখানে এই পদ্ধতিটি বৈধ। (যেমন এটি যখন এপিআই সুবিধা প্রাপ্ত হয় এবং ps
এটি ব্যবহারের জন্য সেটগিড বা সেটুইড থাকে) তবে এখনও সেখানে সম্ভাব্য সম্ভাবনা নেই।
আর একটি উপায় হ'ল গোপনীয়তাটি পাস নাargv[]
করে প্রোগ্রামটি কোডটি ইনজেক্ট করা (ব্যবহার gdb
বা একটি $LD_PRELOAD
হ্যাক) main()
এটি শুরুর আগে যেটি argv[]
থেকে প্রাপ্ত গোপনীয়তা সন্নিবেশ করে execve()
।
সাথে LD_PRELOAD
, একটি GNU সিস্টেমে অ-সেটুইড / সেটগিড গতিশীলভাবে সংযুক্ত এক্সিকিউটেবলের জন্য:
/*
* replace ***** with secret read from fd 9
* gcc -Wall -fpic -shared -o inject_secret.so inject_secret.c -ldl
* LD_PRELOAD=/.../inject_secret.so cmd -p '*****' 9<<< secret
*/
#define _GNU_SOURCE
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dlfcn.h>
#define PLACEHOLDER "*****"
static char secret[1024];
int __libc_start_main(int (*main) (int, char**, char**),
int argc,
char **argv,
void (*init) (void),
void (*fini)(void),
void (*rtld_fini)(void),
void (*stack_end)){
static int (*real_libc_start_main)() = NULL;
int n;
if (!real_libc_start_main) {
real_libc_start_main = dlsym(RTLD_NEXT, "__libc_start_main");
if (!real_libc_start_main) abort();
}
n = read(9, secret, sizeof(secret));
if (n > 0) {
int i;
if (secret[n - 1] == '\n') secret[--n] = '\0';
for (i = 1; i < argc; i++)
if (strcmp(argv[i], PLACEHOLDER) == 0)
argv[i] = secret;
}
return real_libc_start_main(main, argc, argv, init, fini,
rtld_fini, stack_end);
}
তারপর:
$ gcc -Wall -fpic -shared -o inject_secret.so inject_secret.c -ldl
$ LD_PRELOAD=$PWD/inject_secret.so ps '*****' 9<<< "-opid,args"
PID COMMAND
7659 /bin/zsh
8828 ps *****
কোনও অবস্থাতেই সেখানে ps
প্রদর্শিত হবে ps -opid,args
না ( -opid,args
এই উদাহরণের গোপনীয়তা)। নোট করুন যে আমরা পয়েন্টারেরargv[]
অ্যারের উপাদানগুলিকে প্রতিস্থাপন করছি , সেই পয়েন্টারগুলির দ্বারা নির্দেশিত স্ট্রিংগুলিকে ওভাররাইড না করে এটি কারণ আমাদের পরিবর্তনগুলি আউটপুটে প্রদর্শিত হয় না ।ps
সাথে gdb
, এখনও অ-সেটুইড / সেটগিড গতিশীলভাবে সংযুক্ত এক্সিকিউটেবল এবং জিএনইউ সিস্টেমে:
tmp=$(mktemp) && cat << EOF > "$tmp" &&
break __libc_start_main
commands 1
set argv[1]="-opid,args"
continue
end
run
EOF
gdb -n --batch-silent --return-child-result -x "$tmp" --args ps '*****'
rm -f -- "$tmp"
তবুও gdb
, একটি নন-জিএনইউ নির্দিষ্ট পদ্ধতি যা এক্সিকিউটেবলগুলিকে ডায়নামিকভাবে লিঙ্কযুক্ত বা ডিবাগ প্রতীক থাকতে পারে না এবং লিনাক্সে কমপক্ষে কার্যকর হওয়া কোনও ইএলএফের পক্ষে কাজ করা উচিত:
#! /bin/sh -
# gdb+sh polyglot script to replace "*****" arguments with the content
# of the SECRET environment variable *after* execve and before calling
# the executable's main() function.
#
# Usage: SECRET=somesecret cmd --password '*****'
if ':' - ':'
then
# running in sh
# retrieve the start address for the executable
start=$(
LC_ALL=C objdump -f -- "$(command -v -- "${1?}")" |
sed -n 's/^start address //p'
)
[ -n "$start" ] || exit
# re-exec ourself with gdb.
exec gdb -n --batch-silent --return-child-result -iex "set \$start = $start" -x "$0" --args "$@"
exit 1
fi
end
# running in gdb
break *$start
commands 1
# The stack on startup contains:
# argc argv[0]... argv[argc-1] 0 envp[0] envp[1]... 0 argv[] and envp[] strings
set $argc = *((int*)$sp)
set $argv = &((char**)$sp)[1]
set $envp = &($argv[$argc+1])
set $i = 0
while $envp[$i]
# look for an envp[] string starting with "SECRET=". We can't use strcmp()
# here as there's no guarantee that the debugged executable has such
# a function
set $e = $envp[$i]
if $e[0] == 'S' && \
$e[1] == 'E' && \
$e[2] == 'C' && \
$e[3] == 'R' && \
$e[4] == 'E' && \
$e[5] == 'T' && \
$e[6] == '='
set $secret = &($e[7])
# replace SECRET=xxx<NUL> with SECRE=<NUL>
set $e[5] = '='
set $e[6] = '\0'
# not calling loop_break as that causes a SEGV with my version of gdb
end
set $i = $i + 1
end
if $secret
# now looking for argv[] strings being "*****" and replace them with
# the secret identified earlier
set $i = 0
while $i < $argc
set $a = $argv[$i]
if $a[0] == '*' && \
$a[1] == '*' && \
$a[2] == '*' && \
$a[3] == '*' && \
$a[4] == '*' && \
$a[5] == '\0'
set $argv[$i] = $secret
end
set $i = $i + 1
end
end
# using "continue" as "detach" causes a SEGV with my version of gdb.
continue
end
run
স্ট্যাটিকালি লিঙ্কযুক্ত এক্সিকিউটেবলের সাথে পরীক্ষা করা:
$ SECRET=/proc/self/cmdline ./replace_secret busybox cat '*****' | tr '\0' '\n'
/bin/busybox
cat
*****
যখন এক্সিকিউটেবল স্থিতিশীল হতে পারে, তখন গোপনীয়তা সংরক্ষণের জন্য আমাদের কাছে মেমরি বরাদ্দ করার কোনও নির্ভরযোগ্য উপায় নেই, সুতরাং আমাদের অন্য কোথাও থেকে গোপনীয়তাটি পেতে হবে যা ইতিমধ্যে প্রক্রিয়া স্মৃতিতে রয়েছে। এই কারণেই পরিবেশটি এখানে সুস্পষ্ট পছন্দ। প্রক্রিয়াটি কোনও কারণে তার পরিবেশ ডাম্প করার সিদ্ধান্ত নিয়েছে বা অবিশ্বস্ত অ্যাপ্লিকেশনগুলি চালিয়ে যাওয়ার সিদ্ধান্ত নেওয়ার SECRET
জন্য প্রক্রিয়াটি (এটিকে পরিবর্তিত করে SECRE=
) প্রক্রিয়ায় আমরা সেই env বারটিকেও আড়াল করি ।
এটি সোলারিস 11-তেও কাজ করে (প্রদত্ত জিডিবি এবং জিএনইউ বাইনুটিসগুলি ইনস্টল করা আছে (আপনার নাম পরিবর্তন করতে হতে objdump
পারে gobjdump
))।
ফ্রিবিএসডি-তে (কমপক্ষে x86_64, আমি নিশ্চিত নই যে স্ট্যাকের প্রথম 24 বাইটগুলি (যা 16 হয় যখন gdb (8.0.1) ইন্টারেক্টিভ প্রস্তাব দিচ্ছে যে সেখানে স্ট্রাকের কোনও বাগ থাকতে পারে), সংজ্ঞা argc
এবং argv
সংজ্ঞাগুলি প্রতিস্থাপন করুন সঙ্গে:
set $argc = *((int*)($sp + 24))
set $argv = &((char**)$sp)[4]
( gdb
অন্যথায় সিস্টেমের সাথে আসা সংস্করণটি প্রাচীন হিসাবে আপনার প্যাকেজ / পোর্টও ইনস্টল করতে হতে পারে )।