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)