curl ป้องกันรหัสผ่านไม่ให้ปรากฏในเอาต์พุต ps ได้อย่างไร


68

ฉันสังเกตเห็นเมื่อไม่นานมานี้ว่าชื่อผู้ใช้และรหัสผ่านที่กำหนดให้curlเป็นอาร์กิวเมนต์บรรทัดคำสั่งไม่ปรากฏในpsผลลัพธ์ (แม้ว่าแน่นอนว่าพวกเขาอาจปรากฏในประวัติทุบตีของคุณ)

/proc/PID/cmdlineเขาก็จะไม่ปรากฏใน

(ความยาวของอาร์กิวเมนต์ชื่อผู้ใช้ / รหัสผ่านรวมกันสามารถได้รับแม้ว่า)

การสาธิตด้านล่าง:

[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คุณสมบัติหรือเป็นคุณสมบัติเคอร์เนลของการจัดเรียงบางอย่าง?)


นอกจากนี้: สามารถทำได้จากนอกซอร์สโค้ดของไบนารีที่ปฏิบัติการได้หรือไม่? เช่นโดยการใช้คำสั่งเชลล์อาจรวมกับสิทธิ์ root หรือไม่

กล่าวอีกนัยหนึ่งฉันสามารถซ่อนอาร์กิวเมนต์ไม่ให้ปรากฏใน/procหรือในpsผลลัพธ์ (ในสิ่งเดียวกันฉันคิดว่า) ที่ฉันส่งไปยังคำสั่งเชลล์โดยพลการ ? (ฉันเดาว่าคำตอบสำหรับคำถามนี้คือ "ไม่" แต่ดูเหมือนว่าคุ้มค่าที่จะรวมคำถามพิเศษครึ่งหนึ่งนี้)



16
ไม่ได้เป็นคำตอบ แต่ทราบว่าวิธีการนี้จะไม่ปลอดภัย มีหน้าต่างการแข่งขันระหว่างการเริ่มต้นโปรแกรมและการล้างสตริงอาร์กิวเมนต์ในระหว่างที่ผู้ใช้สามารถอ่านรหัสผ่านได้ อย่ายอมรับรหัสผ่านที่ละเอียดอ่อนในบรรทัดคำสั่ง
..

1
เกี่ยวข้องอย่างหลวม: ตัวแปรสภาพแวดล้อมเป็นของใคร และทุกคนในทางปฏิบัติใช้environโดยตรงในการเข้าถึงตัวแปรสภาพแวดล้อมหรือไม่? - บรรทัดล่าง: รายการอาร์กิวเมนต์เช่นรายการตัวแปรสภาพแวดล้อมอยู่ในหน่วยความจำกระบวนการผู้ใช้ในการอ่าน / เขียนและสามารถแก้ไขได้โดยกระบวนการผู้ใช้
สกอตต์

1
@ JPhi1618 เพียงทำให้ตัวละครตัวแรกของgrepแบบของคุณเป็นคลาสตัวละคร เช่นps -ef | grep '[c]url'
ไวด์การ์ด

1
@mpy ไม่ซับซ้อนมาก regexes บางตัวจับคู่กันและบางอันไม่ curlตรงcurlแต่ไม่ตรงกับ[c]url [c]urlหากคุณต้องการรายละเอียดเพิ่มเติมถามคำถามใหม่และฉันยินดีที่จะตอบ
Wildcard

คำตอบ:


78

เมื่อเคอร์เนลดำเนินการโปรเซสมันคัดลอกอาร์กิวเมนต์บรรทัดคำสั่งไปยังหน่วยความจำแบบอ่าน - เขียนที่เป็นของกระบวนการ (บนสแต็กอย่างน้อยบน Linux) กระบวนการสามารถเขียนไปยังหน่วยความจำเช่นเดียวกับหน่วยความจำอื่น ๆ เมื่อpsแสดงอาร์กิวเมนต์มันจะอ่านสิ่งที่ถูกจัดเก็บในที่อยู่นั้นในหน่วยความจำของกระบวนการ โปรแกรมส่วนใหญ่จะเก็บอาร์กิวเมนต์ดั้งเดิมไว้ แต่สามารถเปลี่ยนแปลงได้ คำอธิบาย POSIX ของpsรัฐว่า

มันไม่ได้ระบุว่าสตริงที่แสดงนั้นเป็นเวอร์ชันของรายการอาร์กิวเมนต์ที่ส่งผ่านไปยังคำสั่งเมื่อเริ่มต้นหรือเป็นเวอร์ชันของอาร์กิวเมนต์ตามที่แอปพลิเคชันอาจถูกแก้ไข แอปพลิเคชันไม่สามารถขึ้นอยู่กับความสามารถในการปรับเปลี่ยนรายการอาร์กิวเมนต์ของพวกเขาและการปรับเปลี่ยนนั้นสะท้อนให้เห็นในผลลัพธ์ของ ps

เหตุผลที่กล่าวถึงนี้คือตัวแปร unix ส่วนใหญ่สะท้อนการเปลี่ยนแปลง แต่การใช้งาน POSIX บนระบบปฏิบัติการประเภทอื่นอาจไม่

คุณลักษณะนี้มีการใช้งาน จำกัด เนื่องจากกระบวนการไม่สามารถทำการเปลี่ยนแปลงโดยพลการ อย่างน้อยที่สุดความยาวทั้งหมดของอาร์กิวเมนต์ไม่สามารถเพิ่มได้เนื่องจากโปรแกรมไม่สามารถเปลี่ยนตำแหน่งที่psจะดึงข้อมูลอาร์กิวเมนต์และไม่สามารถขยายพื้นที่เกินขนาดเดิมได้ ความยาวสามารถลดลงได้อย่างมีประสิทธิภาพโดยการใส่ค่า null เป็นจุดสิ้นสุดเนื่องจากอาร์กิวเมนต์เป็นสตริงที่สิ้นสุดด้วยค่า null แบบ C

หากคุณต้องการขุดจริงๆคุณสามารถดูที่มาของการใช้งานโอเพ่นซอร์ส บน Linux, แหล่งที่มาของpsไม่ได้เป็นที่น่าสนใจที่คุณจะเห็นมีก็คือว่ามันอ่านอาร์กิวเมนต์บรรทัดคำสั่งจากระบบแฟ้ม procใน รหัสที่สร้างเนื้อหาของไฟล์นี้อยู่ในเคอร์เนลในใน ส่วนหนึ่งของหน่วยความจำของกระบวนการ (เข้าถึงด้วย) ไปจากที่อยู่ไปที่; ที่อยู่เหล่านี้จะถูกบันทึกไว้ในเคอร์เนลเมื่อกระบวนการเริ่มต้นและไม่สามารถเปลี่ยนแปลงได้ในภายหลัง/proc/PID/cmdlineproc_pid_cmdline_readfs/proc/base.caccess_remote_vmmm->arg_startmm->arg_end

ภูตบางคนใช้ความสามารถในการสะท้อนให้เห็นถึงสถานะของพวกเขาเช่นที่พวกเขาเปลี่ยนพวกเขาargv[1]เป็นสตริงเหมือนstartingหรือหรือavailable exitingหลายตัวแปรยูนิกซ์มีsetproctitleฟังก์ชั่นการทำเช่นนี้ บางโปรแกรมใช้ความสามารถนี้เพื่อซ่อนข้อมูลที่เป็นความลับ โปรดทราบว่านี่เป็นการใช้งานที่ จำกัด เนื่องจากอาร์กิวเมนต์บรรทัดคำสั่งสามารถมองเห็นได้ในขณะที่กระบวนการเริ่มต้นขึ้น

ภาษาระดับสูงส่วนใหญ่คัดลอกอาร์กิวเมนต์ไปยังวัตถุสตริงและไม่ให้วิธีการแก้ไขที่เก็บข้อมูลดั้งเดิม นี่คือโปรแกรม C ที่แสดงความสามารถนี้โดยการเปลี่ยนargvองค์ประกอบโดยตรง

#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.cmemsetที่ใช้ในการเปลี่ยนช่องว่างอาร์กิวเมนต์ทั้งหมดโดยใช้ ในsrc/tool_getparam.cฟังก์ชั่นนี้ใช้ไม่กี่ครั้งเช่นโดยการใส่รหัสผ่านของผู้ใช้ซ้ำ เนื่องจากฟังก์ชันถูกเรียกใช้จากการวิเคราะห์พารามิเตอร์ฟังก์ชันจะเกิดขึ้นในช่วงต้นของการเรียกใช้ curl แต่การทิ้งบรรทัดคำสั่งก่อนที่สิ่งนี้จะยังคงแสดงรหัสผ่านใด ๆ

เนื่องจากอาร์กิวเมนต์จะถูกเก็บไว้ในหน่วยความจำของกระบวนการจึงไม่สามารถเปลี่ยนแปลงได้จากภายนอกยกเว้นโดยใช้ดีบักเกอร์


ที่ดี! ดังนั้นเกี่ยวกับข้อมูลโค้ดฉันเข้าใจว่าหมายความว่ามันจะเป็นไปตาม POSIX เพื่อให้เคอร์เนลของคุณเก็บอาร์กิวเมนต์บรรทัดคำสั่งดั้งเดิมของกระบวนการภายนอกหน่วยความจำการอ่าน - เขียนของกระบวนการ ( นอกเหนือจากการคัดลอกในหน่วยความจำอ่าน - เขียน) ? แล้วมีการpsรายงานข้อโต้แย้งจากหน่วยความจำของเคอร์เนลนั้นโดยไม่สนใจการเปลี่ยนแปลงใด ๆ ที่เกิดขึ้นในหน่วยความจำแบบอ่าน - เขียนของกระบวนการหรือไม่? แต่ (ถ้าฉันเข้าใจถูกต้อง) รูปแบบของ UNIX ส่วนใหญ่ไม่ได้ทำมาก่อนดังนั้นคุณจึงไม่สามารถpsปรับใช้หลังได้หากไม่มีการแก้ไขเคอร์เนลเนื่องจากข้อมูลต้นฉบับไม่ได้ถูกเก็บไว้ที่ไหน?
Wildcard

1
@ บัตรถูกต้องแล้ว อาจมีการใช้ Unix ที่เก็บต้นฉบับ แต่ฉันไม่คิดว่าคนทั่วไปทำ ภาษา C ช่วยให้เนื้อหาของargvรายการจะมีการเปลี่ยนแปลง (คุณไม่สามารถตั้งargv[i]แต่คุณสามารถเขียนargv[i][0]ผ่านargv[i][strlen(argv[i])]) ดังนั้นจะต้องมีการคัดลอกในหน่วยความจำของกระบวนการมี
Gilles

2
ฟังก์ชั่นที่เกี่ยวข้องในซอร์สโค้ดขด: github.com/curl/curl/blob/master/src/tool_paramhlp.c#L139
sebasth

4
@ Wildcard, Solaris ทำสิ่งนี้ บรรทัดคำสั่งที่เห็นโดย / usr / ucb / ps เป็นสำเนาที่เป็นเจ้าของกระบวนการ (ไม่แน่นอน) บรรทัดคำสั่งที่เห็นโดย / usr / bin / ps เป็นสำเนาที่เคอร์เนลเป็นเจ้าของ (ไม่เปลี่ยนรูป) เคอร์เนลจะเก็บ 80 ตัวอักษรแรกเท่านั้น สิ่งอื่นถูกตัดทอน
BowlOfRed

1
@Wildcard จริง ๆ แล้ว null ต่อท้ายเป็นอาร์กิวเมนต์ว่างเปล่า ในpsการส่งออกจำนวนมากของการขัดแย้งที่ว่างเปล่าดูเหมือนว่าไม่มีอะไรมี /proc/PID/cmdlineแต่ใช่มันไม่สร้างความแตกต่างถ้าคุณตรวจสอบว่าหลายพื้นที่มีและคุณสามารถสังเกตเห็นมากขึ้นโดยตรงจาก
Gilles

14

คำตอบอื่น ๆ ตอบคำถามได้ดีในลักษณะทั่วไป หากต้องการตอบโดยเฉพาะ " ผลที่ได้นี้ประสบความสำเร็จได้อย่างไรมันอยู่ที่ไหนสักแห่งในซอร์สโค้ดของขด? ":

ในส่วนของการโต้แย้งการแยกของรหัสที่มาขดที่-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ถูกเขียนทับด้วยช่องว่างตามที่อธิบายโดยคำตอบอื่น ๆ


ฉันชอบความคิดเห็นในcleanargอเมริกาอย่างชัดเจนว่ามันกำลังทำสิ่งที่คำถามกำลังถามอยู่!
Floris

3

กระบวนการไม่เพียง แต่อ่านพารามิเตอร์เท่านั้น แต่ยังสามารถเขียนได้

ฉันไม่ใช่นักพัฒนาดังนั้นฉันไม่คุ้นเคยกับสิ่งนี้ แต่อาจเป็นไปได้จากภายนอกด้วยวิธีการคล้ายกับการเปลี่ยนแปลงของพารามิเตอร์สภาพแวดล้อม:

https://stackoverflow.com/questions/205064/is-there-a-way-to-change-another-processs-environment-variables


โอเค แต่การทำงานเช่นbash -c 'awk 1 /proc/$$/cmdline; set -- something; awk 1 /proc/$$/cmdline'แสดงให้เห็นว่าอย่างน้อยที่สุดในเชลล์การตั้งค่าพารามิเตอร์นั้นแตกต่างจากการปรับเปลี่ยนสิ่งที่เคอร์เนลเห็นว่าเป็นพารามิเตอร์กระบวนการ
สัญลักษณ์แทน

4
@Wildcard ตำแหน่งอาร์กิวเมนต์ในเชลล์สคริปต์เป็นสำเนาเริ่มต้น ของอาร์กิวเมนต์บรรทัดคำสั่งบางส่วนของกระบวนการเชลล์ เชลล์ส่วนใหญ่ไม่ยอมให้สคริปต์เปลี่ยนอาร์กิวเมนต์เดิม
Gilles

@Gilles ใช่นั่นคือจุดของความคิดเห็นของฉัน :) คำแถลงทั่วไปที่กระบวนการสามารถทำได้ (ประโยคแรกของคำตอบนี้) ไม่ตอบว่าจะสามารถทำได้โดยคุณสมบัติเชลล์ที่มีอยู่ คำตอบสำหรับสิ่งนี้ดูเหมือนจะเป็น "ไม่" ซึ่งเป็นสิ่งที่ฉันเดาได้ที่ด้านล่างสุดของคำถาม
Wildcard
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.