The cam is from approx ~2003 -2005? and nearly-never used.
I got this camera in the original cardboard box, but without any documentation. As it is no-name ("Digitus" brand), there is no info on the web because it is old and outdated, too.
"Featurelist":
First step: hello there?
- Laptop listening with wireshark
- webcam connected
- power applied
- traffic detected, it runs on 192.168.2.3
Second Step: Web interface
- Own IP set to 192.168.2.1
- browsing to 192.168.2.3:80
- Login screen, which does only accept 4 letter max. password (wtf?)
- admin / 1234 does the trick
Third step: crappy java applet to view images
- Whole web interface requires Java Script to work, except image display
- Image display is done with "video_java.class"
- decompiled with JAD it reveals:
- open socket to port 4321 (hardcoded in this java file, but configurable web gui parameters!)
- send magic string "0110\n", no auth
- receive 4 byte header
- first 2 bytes = payload size
- second 2 bytes = ??
- receive payload (jpg file) in little junks (else camera can't catch up!)
- display jpg
- after timer triggers, repeat
- crappiest solution to display image in browser. ever.
Fourth step: lets talk to that web gui (or better block that port entirely)
- web gui auth works as follows:
- take username (admin), password (1234) and challenge (more on this later) and run md5 on it
- send md5 ("response") to cam
- cam answers with session cookie
- keep cookie, send it with all requests
- challenge/cookie
- 4 byte string
- generated by:
- internal 24bit counter counts in an endless loop. Starts at zero after power-on.
- represenation of this 24bit value by 4 chars = 6 bit (=64 states) encoded by char
- chars are from these: ./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz and represent 0-63
- there is no way to log out (=timeout mechanism)
- bruteforce for 4 byte string with known values is easy and just limited by slow camera
- triggering some actions / config features
- commands are sent by HTTP POST, including cookie
- most settings are remembered (e.g. LEDs on or off), except image size (defaults to 320x240)
- settings are written to small eeprom on board, which takes approx 10 seconds. Using LEDs as outputs (e.g. to drive IR LEDs) is out of question.
- cam seems to run 320x240px natively, because 160x120 and 640x480 bring the frame rate down (20fps to 5-7fps)
- email server CANNOT be disabled, which means the cam spams with host name requests for "mailserver.com" - setting it to 127.0.0.1 stops that.
"ugly" live image viewer with python/pygame:
#!/usr/bin/env python
import pygame
from pygame.locals import *
from StringIO import StringIO
import time
import socket # Import socket module
def getimg(host="192.168.2.3"):
s = socket.socket() # Create a socket object
port = 4321 # Reserve a port for your service.
s.settimeout(0.1)
rcvbuf=""
try:
s.connect((host, port))
s.send("0110\n") # request image
header = s.recv(4) # first two byte is content length in hex
payload_size = ord(header[0])*256 + ord(header[1])
partsize=1024
for i in range(0,int(payload_size/partsize+1)):
rcvbuf+=s.recv(partsize)
#print "%3i%% (%6i / %6i)" % ((100*(len(rcvbuf)))/(payload_size),len(rcvbuf),payload_size)
time.sleep(0.002)
except:
pass
s.close # Close the socket when done
return rcvbuf # return image as raw string
def main():
pygame.init()
screen = pygame.display.set_mode((640,480))
pygame.display.set_caption("camviewer")
screen.fill((255,255,255))
clock = pygame.time.Clock()
running = True
while running:
# handle events
for event in pygame.event.get():
if event.type == QUIT:
running = False
clock.tick()
pygame.display.set_caption("camviewer - %i fps"%(clock.get_fps()))
# draw image
try:
data = StringIO(getimg())
img = pygame.image.load(data,".JPG")
if img.get_width() != screen.get_width() or img.get_height() != screen.get_height():
screen = pygame.display.set_mode((img.get_width(),img.get_height()))
screen.blit(img,(0,0))
except:
time.sleep(0.1)
pass
pygame.display.flip()
if __name__ == '__main__': main()
config IF access:
#!/usr/bin/env python
from urllib import urlencode
from urllib2 import urlopen, Request
from hashlib import md5
from time import sleep
def taifatech_dec(code):
numbercoding = "./1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
intcode = []
sum = 0
factor = 1
for char in code:
ipos = numbercoding.find(char)
intcode.append( ipos )
sum += ipos * factor
factor *= 64
return sum
def taifatech_enc(integer):
numbercoding = "./1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
code = ""
while integer >0:
code += numbercoding[integer%64]
integer = integer/64
while len(code)<4:
code += "."
return code
def get_challenge():
# first get challenge from server
html = urlopen("http://192.168.2.3").read()
challenge = html[html.find("NAME=\"Challenge\" VALUE=\n\"")+25:][:8]
return challenge
def calc_response(challenge):
username = "admin"
password = "1234"
# calc response
response = md5(username+password+challenge).hexdigest()
return response
def get_cookie(response):
# log in
data = urlencode({"Challenge":"","Password":"","Response":response,"Username":"admin"})
u = urlopen("http://192.168.2.3/tgi/login.tgi",data)
u.read()
# extract cookie
info = str(u.info())
cookie = info[info.find("Taifatech=")+10:][:4]
return cookie
def set_resolution(cookie):
# run script, with cookie
data = urlencode({"Apply":"Apply","D1":"P640","Apply":"Apply"})
r = Request("http://192.168.2.3/tgi/control.tgi",data)
r.add_header("Cookie","Taifatech="+cookie)
u = urlopen(r)
return u.read()
def set_frequency(cookie):
# run script, with cookie
data = urlencode({"hz":"P_50HZ","hz1":"Apply"})
r = Request("http://192.168.2.3/tgi/hz.tgi",data)
r.add_header("Cookie","Taifatech="+cookie)
u = urlopen(r)
return u.read()
def set_led(cookie):
data = urlencode({"led_on_off":""})
r = Request("http://192.168.2.3/tgi/tools.tgi",data)
r.add_header("Cookie","Taifatech="+cookie)
u = urlopen(r)
return u.read()
default_len = 0
if False:
for i in range(0,2**24):
if i%128 == 0:
print 100*float(i)/float(2**24)
cookie = taifatech_enc(i)
retd = set_resolution(cookie)
l= len(retd)
if l != default_len:
default_len = l
print cookie
print retd
challenge = get_challenge()
print "challenge = " + challenge
response = calc_response(challenge)
print "response = " + response
cookie = get_cookie(response)
print "cookie = " + cookie
#print set_frequency(cookie)
#sleep(0.1)
#print set_resolution(cookie)
set_led(cookie)
rawe