ไฟล์ปฏิบัติการ PHP จะทำงานในบรรทัดคำสั่ง แต่ไม่ใช่เบราว์เซอร์

ดังนั้นการใช้ 'php index.php' ทำให้ฉันได้ผลลัพธ์ที่ฉันต้องการในบรรทัดคำสั่ง แต่จะไม่ให้ผลงานบนหน้าเว็บ ก่อนอื่นฉันมีไฟล์ python ซึ่งโดยพื้นฐานแล้วทำทุกอย่างที่ฉันต้องการ:

import subprocess

subprocess.call("sudo nmap -sP 192.168.1.0/24 > /home/pi/whohome.txt", shell=True)

searchfile = open("/home/pi/whohome.txt", "r")
for line in searchfile:
    if "android-5ab6eb374b5fd6" in line: print "Jeremy is home (phone)"
    if "Jeremys-MBP" in line: print "Jeremy is home (computer)"
    if "LMCMs-iPhone" in line: print "Liam is home (phone)"
    if "Liam" in line: print "Liam is home (computer)"
    if "android-4a186cbbeb2c5229" in line: print "Lara is home (phone)"
    if "LaraD" in line: print "Lara is home (computer)"
    if "KristiansiPhone" in line: print "Martin is home (phone)"
    if "Martins-MBP" in line: print "Martin is home (computer)"

searchfile.close()

ประการที่สอง ฉันเพิ่งมีไฟล์ปฏิบัติการ sh ที่จะใส่เอาต์พุตของคำสั่ง python นี้ลงในไฟล์ข้อความอื่น:

python /home/pi/myRoomMates.py > /var/www/html/website.txt

จากนั้นฉันมีไฟล์ php บนเว็บเซิร์ฟเวอร์ apache ที่ทำงานบน raspberry pi มันอ่านว่า:

<?php
shell_exec('/home/pi/whoishome.sh');

echo file_get_contents ("/var/www/html/website.txt");

?>

ดังนั้นถ้าฉันจำไม่ผิด ทุกครั้งที่รีเฟรชเพจ ควรดำเนินการนั้น รอให้ exec ดำเนินการเสร็จสิ้น จากนั้นจึงแสดงเนื้อหาไฟล์ txt? ฉันได้ลองทั้ง shell_exec และเพิ่ง exec ทั้งคู่ก็ทำเหมือนกัน ..


person Jeremy de Rooy    schedule 23.05.2016    source แหล่งที่มา
comment
ฉันกำลังเดิมพันเรื่องการอนุญาต   -  person    schedule 23.05.2016
comment
ฉันกำลังเพิ่มเดิมพันของ @Dagon เป็นสองเท่า   -  person Darren    schedule 23.05.2016
comment
@ดาร์เรน เราไม่สามารถแพ้ได้ ผมมีระบบ   -  person    schedule 23.05.2016
comment
@Dagon โอเค ฉันเพิ่งทำ chmod 777 กับทั้งสองไดเร็กทอรี, ไดเร็กทอรี /home/pi/ และไดเร็กทอรี /var/www/html ซึ่งดูเหมือนว่าจะทำอะไรบางอย่างไปแล้วเนื่องจากตอนนี้ไฟล์ website.txt กำลังเปลี่ยนแปลง แต่ทุกครั้งที่ฉันรีเฟรชหน้าเว็บ ไฟล์ txt จะว่างเปล่า แต่ถ้าฉันรัน php ผ่านบรรทัดคำสั่ง มันจะให้เอาต์พุตที่ถูกต้องในไฟล์ txt   -  person Jeremy de Rooy    schedule 23.05.2016
comment
ฉันไม่รู้ว่าระบบปฏิบัติการของ Raspberry Pi มีส่วนขยายความปลอดภัยของ SELinux หรือไม่ แต่ถ้ามี (และเปิดใช้งานอยู่) อาจเป็นไปได้ว่าไฟล์เอาต์พุตเหล่านั้นอย่างน้อยหนึ่งไฟล์ถูกสร้างขึ้นในบริบทความปลอดภัยที่ทำให้ apache มองไม่เห็น (หรือกระบวนการใดกระบวนการหนึ่ง เช่น python หรือ bash) นั่นอาจอธิบายได้ว่า website.txt กลายเป็นว่างเปล่าได้อย่างไร (หรือดูเหมือนว่างเปล่า) ตรวจสอบบริบทความปลอดภัยของไฟล์ด้วย ls -Z *whatever*   -  person Kevin J. Chase    schedule 23.05.2016
comment
@ KevinJ.Chase เพียงใส่เครื่องหมายคำถามไว้ข้างไฟล์ แต่ค้นหาวิธีปิดการใช้งาน SELinux และทำคำสั่งเหล่านั้น แต่ดูเหมือนว่า SELinux จะไม่อยู่ที่ใดในระบบ   -  person Jeremy de Rooy    schedule 23.05.2016
comment
ไฟล์ website.txt ถูกตัดทอนเหลือ 0 ไบต์จริง ๆ เมื่อคุณโหลดหน้าซ้ำหรือไม่ นั่นหมายถึงสคริปต์ python กำลังทำงานอยู่ แต่สร้างเอาต์พุต 0 ไบต์ มันมีอินพุตที่คุ้มค่าในไฟล์ whohome.txt หรือไม่   -  person Kevin J. Chase    schedule 23.05.2016
comment
@KevinJ.Chase ส่งมอบทั้งคู่ด้วยศูนย์ไบต์หลังจากโหลดเพจใหม่ แต่เมื่อรัน php ในบรรทัดคำสั่ง ทั้งคู่จะมีเอาต์พุตที่ถูกต้อง ดังนั้นคำสั่ง python จึงทำงานเช่นนี้ เพียงแค่สร้างเอาต์พุตเป็น 0..   -  person Jeremy de Rooy    schedule 23.05.2016
comment
@ KevinJ.Chase ขออภัยฉันเพิ่งใหม่กับการเขียนโปรแกรมและสิ่งที่ไม่ใช่ แต่นั่นจะแทนที่ส่วน 'searchfile' หรือไม่ คุณช่วยแสดงให้ฉันดูได้ไหมว่าไฟล์ python จะมีหน้าตาเป็นอย่างไรด้วย subprocess.check_output และฉันจะอ้างถึงผลลัพธ์นั้นเพื่อวางไว้บนเว็บเซิร์ฟเวอร์ได้ที่ไหน?   -  person Jeremy de Rooy    schedule 23.05.2016
comment
ดูคำตอบของฉันสำหรับโค้ดตัวอย่าง   -  person Kevin J. Chase    schedule 23.05.2016
comment
อย่าลืมคืนค่าสิทธิ์ที่เหมาะสมให้กับไฟล์และไดเร็กทอรีทั้งหมดที่คุณ chmoded เป็น 0777 และโดยทั่วไปเพื่อเปิดใช้งานการรักษาความปลอดภัยใดๆ ก็ตามที่คุณปิดใช้งานไว้อีกครั้งในขณะที่แก้ไขปัญหานี้   -  person Kevin J. Chase    schedule 23.05.2016


คำตอบ (2)


มีสิทธิหลายประการ คุณต้องแน่ใจว่า:

  1. ผู้ใช้ apache จะต้องอยู่ในกลุ่ม sudoers
  2. ผู้ใช้ apache ต้องเขียนถึง /home/pi/whohome.txt
  3. ผู้ใช้ apache จะต้องเขียนถึง /var/www/html/website.txt
  4. /home/pi/whoishome.shต้องสามารถเรียกใช้งานได้สำหรับผู้ใช้ apache

สำหรับจุดที่ 1 ถึง 3 โดยปกติแล้วไม่ใช่ความคิดที่ดีที่จะให้สิทธิ์เหล่านี้แก่ผู้ใช้ apache

คุณสามารถทำให้ง่ายขึ้นหากคุณเริ่มสคริปต์ python เป็น CGI:

import subprocess

ADDRESS = "192.168.1.0/24"

USERS = {
    "android-5ab6eb374b5fd6": ("Jeremy", "phone"),
    "Jeremys-MBP": ("Jeremy", "computer"),
    "LMCMs-iPhone": ("Liam", "phone"),
    "Liam": ("Liam", "computer"),
    "android-4a186cbbeb2c5229": ("Lara", "phone"),
    "LaraD": ("Lara", "computer"),
    "KristiansiPhone": ("Martin", "phone"),
    "Martins-MBP": ("Martin", "computer"),
}


nmap = subprocess.Popen(["sudo", "nmap", "-sP", ADDRESS], stdout=subprocess.PIPE)
for line in nmap.stdout:
    for user, name in USERS.items():
        if user in line:
            print "%s is home(%s)" % name
nmap.wait()

ต้องกรอกข้อ 1 และ 4 เท่านั้น

person Daniel    schedule 23.05.2016

ฉันสงสัยว่าปัญหาของคุณคือส่วน sudo ของบรรทัดคำสั่ง nmap หากคุณแทนที่ subprocess.call ด้วย subprocess.check_call ฉันคิดว่าคุณจะพบว่า คำสั่งนั้นทำให้เกิด CalledProcessError

สมมุติว่าบัญชีผู้ใช้ของคุณอยู่ในไฟล์ /etc/sudoers แต่เว็บเซิร์ฟเวอร์ไม่เป็นเช่นนั้น

เนื่องจากสิ่งแรกที่ตัวดำเนินการเปลี่ยนเส้นทางเอาต์พุตของเชลล์ (>) ทำคือตัดทอนไฟล์เอาต์พุต การพยายามรัน nmap ที่ล้มเหลวจะส่งผลให้เกิด whohome.txt เป็นศูนย์ไบต์ สคริปต์ Python ที่เหลือก็ทำเช่นเดียวกันกับ website.txt และสุดท้ายคุณจะไม่มีอะไรแสดงบนเว็บไซต์ของคุณ

โซลูชั่น

ไม่จำเป็นต้องมี sudo

บนเดสก์ท็อป Linux ของฉัน ฉัน ไม่ จำเป็นต้องเรียกใช้ nmap ในฐานะรูท เพื่อทำการสแกน Ping ในเครื่อง หากสิ่งนั้นเกิดขึ้นจริงในระบบของคุณ คุณก็ควรจะปล่อยส่วน sudo ของคำสั่ง nmap ของคุณได้เลย และดำเนินการให้เสร็จสิ้น

แม้ว่าจะมีความแตกต่างกันก็ตาม nmap จะทำการทดสอบแต่ละเป้าหมายอย่างละเอียดมากขึ้น เมื่อ -pS ping Sweep รันโดย root จากหน้าคนเก่า nmap (เน้นเพิ่ม):

-sP (ข้ามการสแกนพอร์ต) .

[...]

ตัวเลือก -sP ส่งคำขอ ICMP echo, TCP SYN ไปยังพอร์ต 443, TCP ACK ไปยังพอร์ต 80 และคำขอประทับเวลา ICMP ตามค่าเริ่มต้น เมื่อดำเนินการโดยผู้ใช้ที่ไม่มีสิทธิพิเศษ เฉพาะแพ็กเก็ต SYN เท่านั้นที่จะถูกส่ง (โดยใช้การโทรเชื่อมต่อ) ไปยังพอร์ต 80 และ 443 บนเป้าหมาย เมื่อผู้ใช้ที่มีสิทธิ์พยายามสแกนเป้าหมาย บนอีเธอร์เน็ตท้องถิ่น คำขอ ARP จะถูกนำมาใช้เว้นแต่จะระบุ --send-ip [...]

เปิดใช้งาน sudo สำหรับเว็บเซิร์ฟเวอร์ของคุณ

หากคุณต้องการข้อมูลเพิ่มเติมนี้ (และดูเหมือนว่าคุณต้องการ) คุณจะต้องเรียกใช้ nmap (หรือสคริปต์ Python ที่เรียกข้อมูลนั้น) ด้วยสิทธิ์ผู้ใช้ระดับสูง ฉันไม่เคยพยายามบังคับให้เว็บเซิร์ฟเวอร์ทำเช่นนี้ แต่ฉันคิดว่าอย่างน้อยคุณจะต้องเพิ่มผู้ใช้ของเว็บเซิร์ฟเวอร์ของคุณไปที่ /etc/sudoers สิ่งที่ต้องการ:

apache    localhost=/usr/bin/nmap -sP

or:

httpd    ALL=/usr/local/bin/nmap

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

สร้างไฟล์ปฏิบัติการ SUID เพื่อเรียกใช้ nmap สำหรับคุณ

อีกทางหนึ่ง (และฉันเกลียดตัวเองที่แนะนำสิ่งนี้ --- ต้อง เป็นวิธีที่ดีกว่า) คือการเขียนโปรแกรม SUID (Set User ID) เล็ก ๆ ที่รัน เท่านั้น nmap คำสั่งที่คุณต้องการ นี่คือโปรแกรม C ที่จะทำ:

#include <stdio.h>
#include <unistd.h>

int main(void);

int main(void) {
    int retval = 0;
    char* const error_string = "ERROR: Failed to execute \"/usr/bin/map\"";
    char* const nmap_args[] = {
      "/usr/bin/nmap",
      "-sP",
      "192.168.1.0/24",
      NULL
    };

    retval = execv("/usr/bin/nmap", nmap_args);
    /* execv returns _only_ if it fails, so if we've reached this
     * point, print an error and exit.
     */
    perror(error_string);
    return retval;
}

บันทึกข้างต้นเป็นบางอย่างเช่น nmap_lan.c และคอมไพล์ด้วย:

$ gcc -Wall -o nmap_lan nmap_lan.c

จากนั้น ย้ายไปที่ใดก็ตามที่คุณเก็บสคริปต์ของเว็บไซต์ของคุณ และ ในฐานะ root ให้เปลี่ยนความเป็นเจ้าของและการอนุญาต:

# chown root:root nmap_lan  # Or whatever group name you use.
# chmod 4555 nmap_lan

4 นำหน้าตั้งค่าบิต SUID สี ls ของไดเร็กทอรีอาจแสดงไฟล์นั้นที่ถูกไฮไลต์ การอนุญาตควรมีลักษณะเช่นนี้:

# ls -l nmap_lan
-r-sr-xr-x. 1 root root 6682 May 23 03:04 nmap_lan

ผู้ใช้ใดๆ ที่เรียกใช้ nmap_lan จะได้รับการเลื่อนระดับให้เป็นเจ้าของไฟล์ nmap_lan ชั่วคราว (ในกรณีนี้คือ root) จนกว่าโปรแกรมจะออก นั่นเป็นเรื่องดีเป็นพิเศษ ซึ่งเป็นเหตุผลว่าทำไมฉันถึงฮาร์ดโค้ด ทุกอย่าง ในโปรแกรมนั้น... หากต้องการเปลี่ยนแปลงอะไรก็ตามที่มันทำได้ --- แม้แต่ช่วง IP ที่จะสแกน --- คุณจะต้องแก้ไข nmap_lan.c ไฟล์ คอมไพล์ใหม่ และติดตั้งใหม่

ฉันได้ทดสอบ nmap_lan บนบรรทัดคำสั่งของฉันแล้ว และมันสร้างเอาต์พุตผู้ใช้ที่มีสิทธิ์พิเศษ nmap เมื่อเรียกใช้โดยผู้ใช้ที่ไม่มีสิทธิ์ซึ่งปกติจะได้รับเอาต์พุตที่จำกัดเท่านั้น

ความคิดเห็นเกี่ยวกับสคริปต์ Python

โดยทั่วไปแล้ว Python นั้นอย่างมากในการแยกวิเคราะห์อาร์กิวเมนต์ของเชลล์มากกว่าเชลล์ (ค่าเริ่มต้นสำหรับ shell คือ False ด้วยเหตุผลบางประการ) ดังนั้นให้สคริปต์ Python ของคุณทำงานให้มากที่สุดเท่าที่จะเป็นไปได้ รวมถึง การแยกวิเคราะห์คำสั่งเชลล์ การเปลี่ยนทิศทางอินพุต และเอาต์พุตการเปลี่ยนทิศทาง

ข้อได้เปรียบที่สำคัญของการทำงานใน Python ก็คือ การไม่เปิด อ่าน เขียน หรือปิดไฟล์ใดๆ ของคุณจะส่งผลให้เกิดการหยุดทำงานทันทีและการติดตามสแต็ก --- แทนที่จะเป็นความล้มเหลวแบบเงียบๆ ที่คุณกำลังเผชิญอยู่

ฉันจะเขียนคำสั่ง call ใหม่เพื่อใช้รายการอาร์กิวเมนต์ที่แยกออกจากกันอย่างชัดเจน คุณสามารถจัดการการเปลี่ยนเส้นทางเอาต์พุตได้โดยส่งสตรีมไฟล์ที่เปิดไปยังพารามิเตอร์ stdout คุณสามารถกำจัดการเปลี่ยนเส้นทางเชลล์บิตสุดท้ายของคุณได้โดยให้ Python เปิดไฟล์เอาท์พุตของคุณและเขียนลงไปอย่างชัดเจน

nmap_file='/home/pi/whohome.txt'
with open(nmap_file, 'wt', encoding='ascii') as fout:
    subprocess.call(
      ['/usr/bin/nmap', '-sP', '192.168.1.0/24'],  # Or just ['nmap_lan']
      stdout=fout,
      universal_newlines=True,
      )

output_file='/var/www/html/website.txt'
with open(nmap_file, 'rt', encoding='ascii') as fin:
    with open(output_file, 'wt', encoding='ascii') as fout:
        for line in fin:
            ...
            print('Output here', file=fout)  # Add `file=...` to each print.

นอกจากนี้ เว้นแต่ว่าคุณต้องการไฟล์ whohome.txt นั้นสำหรับอย่างอื่น คุณสามารถกำจัดมันทั้งหมดได้โดยใช้ check_output เพื่อจัดเก็บเอาต์พุตจากคำสั่ง nmap เป็นสตริง จากนั้นแยกออกเป็นบรรทัดแยกกัน (พารามิเตอร์ universal_newlines ยังจัดการการแปลงวัตถุ bytes เป็น str อย่างน้อยใน Python 3)

lines = subprocess.check_output(
  ['/usr/bin/nmap', '-sP', '192.168.1.0/24'],  # Or just ['nmap_lan']
  universal_newlines=True
  ).split('\n')

output_file='/var/www/html/website.txt'
with open(output_file, 'wt', encoding='ascii') as fout:
    for line in lines:
        ...
        print('Output here', file=fout)  # Add `file=...` to each print.

โปรดทราบว่าฉันใช้ with บล็อกเพื่อปิดไฟล์ฟรี

(ท้ายที่สุด ชุดคำสั่ง if นั้นกำลังร้องขอให้เขียนใหม่เป็น for machine in machines_dict: ลูป โดยมีสตริงที่คุณกำลังค้นหาเป็นคีย์ในพจนานุกรมนั้น และเอาต์พุตที่คุณต้องการพิมพ์เป็นค่า)

person Kevin J. Chase    schedule 23.05.2016
comment
ตำนานที่แน่นอน คุณเข้าใจถูกแล้ว มันเป็นคำสั่ง sudo ไม่แน่ใจเลยว่าทำไมฉันถึงมี sudo สำหรับ nmap เพราะมันใช้งานได้โดยไม่ต้องใช้ แต่การลบ sudo และทุกอย่างก็ใช้งานได้ ขอบคุณเควิน! - person Jeremy de Rooy; 23.05.2016
comment
สิ่งเดียวคือจำเป็นต้องใช้การเรียกใช้ nmap เนื่องจาก sudo เนื่องจากอุปกรณ์บางตัวไม่แสดงบนการสแกน nmap หากไม่ได้ทำงานเป็น sudo - person Jeremy de Rooy; 23.05.2016
comment
@JeremydeRooy: ฉันได้อัปเดตคำตอบแล้ว ฉันไม่สามารถทดสอบ sudo ภายใต้ Apache ได้อย่างง่ายดายหรือเชื่อถือได้ในการตั้งค่าที่ฉันมี ดังนั้นฉันจึงไม่สามารถทำอะไรได้มากไปกว่าชี้ไปที่ /etc/sudoers (และอาจรวมไฟล์ไว้ต่ำกว่า /etc/sudoers.d/) และแนะนำว่าผู้ใช้ Apache ของคุณต้องการสิทธิ์ในการเรียกใช้ nmap หรือสคริปต์ Python ของคุณผ่าน sudo (อีกทางเลือกหนึ่งคือเขียนโปรแกรม Set-User-ID (SUID) เล็กๆ ที่รัน เท่านั้น nmap -sP ... แต่คุณต้องการทำเช่นนั้นในภาษาที่คอมไพล์ เช่น C สคริปต์ SUID นั้นยากมาก ปลอดภัย (และโดยทั่วไปแล้วสคริปต์ SUID shell นั้นไม่สามารถรักษาความปลอดภัยได้) - person Kevin J. Chase; 23.05.2016
comment
@JeremydeRooy: ฉันได้เพิ่มโปรแกรม SUID ลงในคำตอบของฉันซึ่งเป็นวิธีแก้ปัญหาที่เป็นไปได้ที่สาม (แต่ฉัน เกลียด มันต้องมีวิธีในการทำเช่นนี้โดยใช้ Apache หรือ PHP หรือ sudo หรือ... อย่างอื่น) - person Kevin J. Chase; 23.05.2016
comment
นี่เป็นคำตอบที่น่าประทับใจ - เยี่ยมมาก! - person Darren; 24.05.2016