Session covering YMCS setup, Yealink phone scanner tool development, and Peaceful Spirit UCG Ultra speed diagnostics (ECM crash-loop, Cox plant issue). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
339 lines
33 KiB
HTML
339 lines
33 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>Yealink Serial Scanner</title>
|
|
<style>
|
|
body { font-family: Consolas, monospace; background: #1e1e1e; color: #d4d4d4; padding: 20px; }
|
|
h1 { color: #569cd6; }
|
|
.form-group { margin: 10px 0; }
|
|
label { display: inline-block; width: 120px; color: #9cdcfe; }
|
|
input, textarea { background: #2d2d2d; color: #d4d4d4; border: 1px solid #555; padding: 5px 10px; font-family: Consolas, monospace; }
|
|
input[type=text], input[type=password] { width: 300px; }
|
|
textarea { width: 500px; height: 100px; }
|
|
button { background: #0e639c; color: white; border: none; padding: 8px 20px; cursor: pointer; font-family: Consolas, monospace; margin: 5px; }
|
|
button:hover { background: #1177bb; }
|
|
button:disabled { background: #555; cursor: not-allowed; }
|
|
#log { background: #0d0d0d; padding: 15px; margin-top: 20px; max-height: 500px; overflow-y: auto; white-space: pre-wrap; font-size: 13px; border: 1px solid #333; }
|
|
.success { color: #4ec9b0; }
|
|
.error { color: #f44747; }
|
|
.info { color: #569cd6; }
|
|
.warn { color: #ce9178; }
|
|
table { border-collapse: collapse; margin-top: 15px; }
|
|
th, td { border: 1px solid #555; padding: 6px 12px; text-align: left; }
|
|
th { background: #264f78; }
|
|
#results { margin-top: 20px; }
|
|
.note { color: #808080; font-size: 12px; margin-top: 5px; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>Yealink Phone Serial Scanner</h1>
|
|
<p class="note">This tool logs into Yealink phone web UIs to extract serial numbers (Machine IDs) for YMCS/RPS registration.
|
|
<br>Enter phone IPs (one per line or comma-separated), credentials, and click Scan.</p>
|
|
|
|
<div class="form-group">
|
|
<label>Phone IPs:</label>
|
|
<textarea id="ips" placeholder="172.16.1.29 172.16.1.58 or: 172.16.1.29, 172.16.1.58">172.16.1.29</textarea>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Username:</label>
|
|
<input type="text" id="username" value="admin">
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Password:</label>
|
|
<input type="password" id="password" value="">
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Site Name:</label>
|
|
<input type="text" id="sitename" value="GuruHQ">
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Protocol:</label>
|
|
<select id="protocol">
|
|
<option value="https">HTTPS</option>
|
|
<option value="http">HTTP</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<button id="scanBtn" onclick="startScan()">Scan Phones</button>
|
|
<button onclick="exportCSV()">Export CSV</button>
|
|
<button onclick="clearLog()">Clear Log</button>
|
|
</div>
|
|
|
|
<div id="log"></div>
|
|
<div id="results"></div>
|
|
|
|
<script>
|
|
// CryptoJS AES + MD5 (minimal inline build)
|
|
// Source: CryptoJS v3.1.2 (MIT License)
|
|
var CryptoJS=CryptoJS||function(u,p){var d={},l=d.lib={},s=function(){},t=l.Base={extend:function(a){s.prototype=this;var c=new s;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}},r=l.WordArray=t.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=p?c:4*a.length},toString:function(a){return(a||v).stringify(this)},concat:function(a){var c=this.words,e=a.words,j=this.sigBytes;a=a.sigBytes;this.clamp();if(j%4)for(var k=0;k<a;k++)c[j+k>>>2]|=(e[k>>>2]>>>24-8*(k%4)&255)<<24-8*((j+k)%4);else for(k=0;k<a;k+=4)c[j+k>>>2]=e[k>>>2];this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<<32-8*(c%4);a.length=u.ceil(c/4)},clone:function(){var a=t.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],e=0;e<a;e+=4)c.push(4294967296*u.random()|0);return new r.init(c,a)}}),w=d.enc={},v=w.Hex={stringify:function(a){var c=a.words;a=a.sigBytes;for(var e=[],j=0;j<a;j++){var k=c[j>>>2]>>>24-8*(j%4)&255;e.push((k>>>4).toString(16));e.push((k&15).toString(16))}return e.join("")},parse:function(a){for(var c=a.length,e=[],j=0;j<c;j+=2)e[j>>>3]|=parseInt(a.substr(j,2),16)<<24-4*(j%8);return new r.init(e,c/2)}},b=w.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var e=[],j=0;j<a;j++)e.push(String.fromCharCode(c[j>>>2]>>>24-8*(j%4)&255));return e.join("")},parse:function(a){for(var c=a.length,e=[],j=0;j<c;j++)e[j>>>2]|=(a.charCodeAt(j)&255)<<24-8*(j%4);return new r.init(e,c)}},x=w.Utf8={stringify:function(a){try{return decodeURIComponent(escape(b.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data")}},parse:function(a){return b.parse(unescape(encodeURIComponent(a)))}},q=l.BufferedBlockAlgorithm=t.extend({reset:function(){this._data=new r.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=x.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,e=c.words,j=c.sigBytes,k=this.blockSize,b=j/(4*k),b=a?u.ceil(b):u.max((b|0)-this._minBufferSize,0);a=b*k;j=u.min(4*a,j);if(a){for(var q=0;q<a;q+=k)this._doProcessBlock(e,q);q=e.splice(0,a);c.sigBytes-=j}return new r.init(q,j)},clone:function(){var a=t.clone.call(this);a._data=this._data.clone();return a}}),G=l.Hasher=q.extend({cfg:t.extend(),init:function(a){this.cfg=this.cfg.extend(a);this.reset()},reset:function(){q.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);return this._doFinalize()},blockSize:16,_createHelper:function(a){return function(c,e){return(new a.init(e)).finalize(c)}},_createHmacHelper:function(a){return function(c,e){return(new n.HMAC.init(a,e)).finalize(c)}}});var n=d.algo={};return d}(Math);
|
|
|
|
// MD5
|
|
(function(u){function p(b,n,a,c,e,j,k){b=b+(n&a|~n&c)+e+k;return(b<<j|b>>>32-j)+n}function d(b,n,a,c,e,j,k){b=b+(n&c|a&~c)+e+k;return(b<<j|b>>>32-j)+n}function l(b,n,a,c,e,j,k){b=b+(n^a^c)+e+k;return(b<<j|b>>>32-j)+n}function s(b,n,a,c,e,j,k){b=b+(a^(n|~c))+e+k;return(b<<j|b>>>32-j)+n}var t=CryptoJS,r=t.lib,w=r.WordArray,v=r.Hasher,r=t.algo,b=[];(function(){for(var n=0;64>n;n++)b[n]=4294967296*u.abs(u.sin(n+1))|0})();r=r.MD5=v.extend({_doReset:function(){this._hash=new w.init([1732584193,4023233417,2562383102,271733878])},_doProcessBlock:function(n,a){for(var c=0;16>c;c++){var e=a+c,j=n[e];n[e]=(j<<8|j>>>24)&16711935|(j<<24|j>>>8)&4278255360}var c=this._hash.words,e=n[a+0],j=n[a+1],k=n[a+2],q=n[a+3],G=n[a+4],H=n[a+5],I=n[a+6],J=n[a+7],K=n[a+8],L=n[a+9],M=n[a+10],N=n[a+11],O=n[a+12],P=n[a+13],Q=n[a+14],R=n[a+15],f=c[0],m=c[1],g=c[2],h=c[3],f=p(f,m,g,h,e,7,b[0]),h=p(h,f,m,g,j,12,b[1]),g=p(g,h,f,m,k,17,b[2]),m=p(m,g,h,f,q,22,b[3]),f=p(f,m,g,h,G,7,b[4]),h=p(h,f,m,g,H,12,b[5]),g=p(g,h,f,m,I,17,b[6]),m=p(m,g,h,f,J,22,b[7]),f=p(f,m,g,h,K,7,b[8]),h=p(h,f,m,g,L,12,b[9]),g=p(g,h,f,m,M,17,b[10]),m=p(m,g,h,f,N,22,b[11]),f=p(f,m,g,h,O,7,b[12]),h=p(h,f,m,g,P,12,b[13]),g=p(g,h,f,m,Q,17,b[14]),m=p(m,g,h,f,R,22,b[15]),f=d(f,m,g,h,j,5,b[16]),h=d(h,f,m,g,I,9,b[17]),g=d(g,h,f,m,N,14,b[18]),m=d(m,g,h,f,e,20,b[19]),f=d(f,m,g,h,H,5,b[20]),h=d(h,f,m,g,M,9,b[21]),g=d(g,h,f,m,R,14,b[22]),m=d(m,g,h,f,G,20,b[23]),f=d(f,m,g,h,L,5,b[24]),h=d(h,f,m,g,Q,9,b[25]),g=d(g,h,f,m,q,14,b[26]),m=d(m,g,h,f,K,20,b[27]),f=d(f,m,g,h,P,5,b[28]),h=d(h,f,m,g,k,9,b[29]),g=d(g,h,f,m,J,14,b[30]),m=d(m,g,h,f,O,20,b[31]),f=l(f,m,g,h,H,4,b[32]),h=l(h,f,m,g,K,11,b[33]),g=l(g,h,f,m,N,16,b[34]),m=l(m,g,h,f,Q,23,b[35]),f=l(f,m,g,h,j,4,b[36]),h=l(h,f,m,g,G,11,b[37]),g=l(g,h,f,m,J,16,b[38]),m=l(m,g,h,f,M,23,b[39]),f=l(f,m,g,h,P,4,b[40]),h=l(h,f,m,g,e,11,b[41]),g=l(g,h,f,m,q,16,b[42]),m=l(m,g,h,f,I,23,b[43]),f=l(f,m,g,h,L,4,b[44]),h=l(h,f,m,g,O,11,b[45]),g=l(g,h,f,m,R,16,b[46]),m=l(m,g,h,f,k,23,b[47]),f=s(f,m,g,h,e,6,b[48]),h=s(h,f,m,g,J,10,b[49]),g=s(g,h,f,m,Q,15,b[50]),m=s(m,g,h,f,H,21,b[51]),f=s(f,m,g,h,O,6,b[52]),h=s(h,f,m,g,q,10,b[53]),g=s(g,h,f,m,M,15,b[54]),m=s(m,g,h,f,j,21,b[55]),f=s(f,m,g,h,K,6,b[56]),h=s(h,f,m,g,R,10,b[57]),g=s(g,h,f,m,I,15,b[58]),m=s(m,g,h,f,P,21,b[59]),f=s(f,m,g,h,G,6,b[60]),h=s(h,f,m,g,N,10,b[61]),g=s(g,h,f,m,k,15,b[62]),m=s(m,g,h,f,L,21,b[63]);c[0]=c[0]+f|0;c[1]=c[1]+m|0;c[2]=c[2]+g|0;c[3]=c[3]+h|0},_doFinalize:function(){var b=this._data,n=b.words,a=8*this._nDataBytes,c=8*b.sigBytes;n[c>>>5]|=128<<24-c%32;var e=u.floor(a/4294967296);n[(c+64>>>9<<4)+14]=(e<<8|e>>>24)&16711935|(e<<24|e>>>8)&4278255360;n[(c+64>>>9<<4)+15]=(a<<8|a>>>24)&16711935|(a<<24|a>>>8)&4278255360;b.sigBytes=4*(n.length+1);this._process();b=this._hash;n=b.words;for(a=0;4>a;a++)c=n[a],n[a]=(c<<8|c>>>24)&16711935|(c<<24|c>>>8)&4278255360;return b}});t.MD5=v._createHelper(r);t.HmacMD5=v._createHmacHelper(r)})(Math);
|
|
|
|
// AES
|
|
(function(){var u=CryptoJS,p=u.lib.BlockCipher,d=u.algo,l=[],s=[],t=[],r=[],w=[],v=[],b=[],x=[],q=[],G=[];(function(){for(var c=[],j=0;256>j;j++)c[j]=128>j?j<<1:j<<1^283;for(var a=0,k=0,j=0;256>j;j++){var e=k^k<<1^k<<2^k<<3^k<<4,e=e>>>8^e&255^99;l[a]=e;s[e]=a;var n=c[a],H=c[n],I=c[H],J=257*c[e]^16843008*e;t[a]=J<<24|J>>>8;r[a]=J<<16|J>>>16;w[a]=J<<8|J>>>24;v[a]=J;J=16843009*I^65537*H^257*n^16843008*a;b[e]=J<<24|J>>>8;x[e]=J<<16|J>>>16;q[e]=J<<8|J>>>24;G[e]=J;a?(a=n^c[c[c[I^n]]],k^=c[c[k]]):a=k=1}})();var H=[0,1,2,4,8,16,32,64,128,27,54],d=d.AES=p.extend({_doReset:function(){for(var c=this._key,j=c.words,a=c.sigBytes/4,c=4*((this._nRounds=a+6)+1),k=this._keySchedule=[],e=0;e<c;e++)if(e<a)k[e]=j[e];else{var n=k[e-1];e%a?6<a&&4==e%a&&(n=l[n>>>24]<<24|l[n>>>16&255]<<16|l[n>>>8&255]<<8|l[n&255]):(n=n<<8|n>>>24,n=l[n>>>24]<<24|l[n>>>16&255]<<16|l[n>>>8&255]<<8|l[n&255],n^=H[e/a|0]<<24);k[e]=k[e-a]^n}j=this._invKeySchedule=[];for(a=0;a<c;a++)e=c-a,n=a%4?k[e]:k[e-4],j[a]=4>a||4>=e?n:b[l[n>>>24]]^x[l[n>>>16&255]]^q[l[n>>>8&255]]^G[l[n&255]]},encryptBlock:function(c,j){this._doCryptBlock(c,j,this._keySchedule,t,r,w,v,l)},decryptBlock:function(c,j){var a=c[j+1];c[j+1]=c[j+3];c[j+3]=a;this._doCryptBlock(c,j,this._invKeySchedule,b,x,q,G,s);a=c[j+1];c[j+1]=c[j+3];c[j+3]=a},_doCryptBlock:function(c,j,a,e,n,b,d,l){for(var f=this._nRounds,m=c[j]^a[0],g=c[j+1]^a[1],h=c[j+2]^a[2],k=c[j+3]^a[3],p=4,q=1;q<f;q++)var r=e[m>>>24]^n[g>>>16&255]^b[h>>>8&255]^d[k&255]^a[p++],s=e[g>>>24]^n[h>>>16&255]^b[k>>>8&255]^d[m&255]^a[p++],t=e[h>>>24]^n[k>>>16&255]^b[m>>>8&255]^d[g&255]^a[p++],k=e[k>>>24]^n[m>>>16&255]^b[g>>>8&255]^d[h&255]^a[p++],m=r,g=s,h=t;r=(l[m>>>24]<<24|l[g>>>16&255]<<16|l[h>>>8&255]<<8|l[k&255])^a[p++];s=(l[g>>>24]<<24|l[h>>>16&255]<<16|l[k>>>8&255]<<8|l[m&255])^a[p++];t=(l[h>>>24]<<24|l[k>>>16&255]<<16|l[m>>>8&255]<<8|l[g&255])^a[p++];k=(l[k>>>24]<<24|l[m>>>16&255]<<16|l[g>>>8&255]<<8|l[h&255])^a[p++];c[j]=r;c[j+1]=s;c[j+2]=t;c[j+3]=k}})})();
|
|
|
|
// CBC mode
|
|
CryptoJS.mode=CryptoJS.mode||{};
|
|
CryptoJS.mode.CBC=function(){var u=CryptoJS.lib.BlockCipherMode.extend();u.Encryptor=u.extend({processBlock:function(p,d){var l=this._cipher,s=l.blockSize;this._iv&&(this._prevBlock=this._iv,this._iv=void 0);for(var t=0;t<s;t++)p[d+t]^=this._prevBlock[t];l.encryptBlock(p,d);this._prevBlock=p.slice(d,d+s)}});u.Decryptor=u.extend({processBlock:function(p,d){var l=this._cipher,s=l.blockSize,t=p.slice(d,d+s);l.decryptBlock(p,d);for(var r=0;r<s;r++)p[d+r]^=this._prevBlock[r];this._prevBlock=t}});return u}();
|
|
|
|
// BlockCipherMode base
|
|
CryptoJS.lib.BlockCipherMode=CryptoJS.lib.Base.extend({createEncryptor:function(u,p){return this.Encryptor.create(u,p)},createDecryptor:function(u,p){return this.Decryptor.create(u,p)},init:function(u,p){this._cipher=u;this._iv=p}});
|
|
|
|
// Cipher base
|
|
(function(){var u=CryptoJS,p=u.lib,d=p.Base,l=p.WordArray,s=u.enc;u.lib.Cipher=d.extend({cfg:d.extend(),createEncryptor:function(a,c){return this.create(1,a,c)},createDecryptor:function(a,c){return this.create(2,a,c)},init:function(a,c,e){this.cfg=this.cfg.extend(e);this._xformMode=a;this._key=c;this.reset()},reset:function(){d.reset.call(this);this._doReset()},process:function(a){this._append(a);return this._process()},finalize:function(a){a&&this._append(a);return this._doFinalize()},_createHelper:function(){function a(c,e,j){if("string"==typeof e)return b(c,e,j);return n(c,e,j)}function n(a,c,e){return a.createEncryptor(c,e).finalize()}function b(a,c,e){e=e||{};var j=u.algo.EvpKDF.create(e.kdf&&e.kdf.cfg||{});c=j.compute(c,e.salt||l.create());e.iv=e.iv||c.clone();return n(a,c,e)}return{encrypt:a,decrypt:function(a,c,e){"string"==typeof c?c=s.Hex.parse(c):void 0;return a.createDecryptor(c.key,e).finalize(c.ciphertext)}}}()});
|
|
u.lib.BlockCipher=u.lib.Cipher.extend({cfg:u.lib.Cipher.cfg.extend({mode:CryptoJS.mode.CBC,padding:{pad:function(a,c){var e=4*c,j=e-a.sigBytes%e;a.concat(l.create([j<<24|j<<16|j<<8|j,j<<24|j<<16|j<<8|j,j<<24|j<<16|j<<8|j,j<<24|j<<16|j<<8|j],j))},unpad:function(a){a.sigBytes-=a.words[a.sigBytes-1>>>2]&255}}}),reset:function(){u.lib.Cipher.reset.call(this);var a=this.cfg,c=a.iv;a=a.mode;if(1==this._xformMode)var e=a.createEncryptor;else e=a.createDecryptor,this._minBufferSize=1;this._mode=e.call(a,this,c&&c.words)},_doProcessBlock:function(a,c){this._mode.processBlock(a,c)},_doFinalize:function(){var a=this.cfg.padding;1==this._xformMode?(a.pad(this._data,this.blockSize),this._process(!0)):(this._process(!0),a.unpad(this._data));return this._data}})})();
|
|
|
|
// ZeroPadding
|
|
CryptoJS.pad=CryptoJS.pad||{};
|
|
CryptoJS.pad.ZeroPadding={pad:function(u,p){var d=4*p;u.clamp();u.sigBytes+=d-(u.sigBytes%d||d)},unpad:function(u){for(var p=u.words,d=u.sigBytes-1;!(p[d>>>2]>>>24-8*(d%4)&255);)d--;u.sigBytes=d+1}};
|
|
|
|
// RSA (minimal JSBN implementation)
|
|
function BigInteger(a,b,c){null!=a&&("number"==typeof a?this.fromNumber(a,b,c):null==b&&"string"!=typeof a?this.fromString(a,256):this.fromString(a,b))}var dbits,canary=0xdeadbeefcafe,j_lm=15715070==(canary&16777215);function am3(a,b,c,d,e,f){for(var g=b&16383,h=b>>14;--f>=0;){var i=this[a]&16383,j=this[a++]>>14,k=h*i+j*g;i=g*i+((k&16383)<<14)+c[d]+e;e=(i>>28)+(k>>14)+h*j;c[d++]=i&268435455}return e}BigInteger.prototype.am=am3;dbits=28;BigInteger.prototype.DB=dbits;BigInteger.prototype.DM=(1<<dbits)-1;BigInteger.prototype.DV=1<<dbits;var BI_FP=52;BigInteger.prototype.FV=Math.pow(2,BI_FP);BigInteger.prototype.F1=BI_FP-dbits;BigInteger.prototype.F2=2*dbits-BI_FP;var BI_RM="0123456789abcdefghijklmnopqrstuvwxyz",BI_RC=[];var rr,vv;rr="0".charCodeAt(0);for(vv=0;vv<=9;++vv)BI_RC[rr++]=vv;rr="a".charCodeAt(0);for(vv=0;vv<26;++vv)BI_RC[rr++]=vv;rr="A".charCodeAt(0);for(vv=0;vv<26;++vv)BI_RC[rr++]=vv;function int2char(a){return BI_RM.charAt(a)}function intAt(a,b){var c=BI_RC[a.charCodeAt(b)];return null==c?-1:c}function bnpCopyTo(a){for(var b=this.t-1;b>=0;--b)a[b]=this[b];a.t=this.t;a.s=this.s}function bnpFromInt(a){this.t=1;this.s=a<0?-1:0;a>0?this[0]=a:a<-1?this[0]=a+this.DV:this.t=0}function nbv(a){var b=new BigInteger(null);b.fromInt(a);return b}function bnpFromString(a,b){var c;if(16==b)c=4;else if(8==b)c=3;else if(256==b)c=8;else if(2==b)c=1;else if(32==b)c=5;else if(4==b)c=2;else{this.fromRadix(a,b);return}this.t=0;this.s=0;for(var d=a.length,e=!1,f=0;--d>=0;){var g=8==c?a.charCodeAt(d)&255:intAt(a,d);g<0?"-"==a.charAt(d)&&(e=!0):(e=!1,0==f?this[this.t++]=g:f+c>this.DB?(this[this.t-1]|=(g&(1<<this.DB-f)-1)<<f,this[this.t++]=g>>this.DB-f):this[this.t-1]|=g<<f,f+=c,f>=this.DB&&(f-=this.DB))}8==c&&0!=(a.charCodeAt(0)&128)&&(this.s=-1,f>0&&(this[this.t-1]|=(1<<this.DB-f)-1<<f));this.clamp();e&&BigInteger.ZERO.subTo(this,this)}function bnpClamp(){for(var a=this.s&this.DM;this.t>0&&this[this.t-1]==a;)--this.t}function bnToString(a){if(this.s<0)return"-"+this.negate().toString(a);var b;if(16==a)b=4;else if(8==a)b=3;else if(2==a)b=1;else if(32==a)b=5;else if(4==a)b=2;else return this.toRadix(a);var c=(1<<b)-1,d,e=!1,f="",g=this.t;var h=this.DB-g*this.DB%b;if(g-- >0){if(h<this.DB&&(d=this[g]>>h)>0){e=!0;f=int2char(d)}for(;g>=0;)h<b?(d=(this[g]&(1<<h)-1)<<b-h,d|=this[--g]>>(h+=this.DB-b)):(d=this[g]>>(h-=b)&c,h<=0&&(h+=this.DB,--g)),d>0&&(e=!0),e&&(f+=int2char(d))}return e?f:"0"}function bnNegate(){var a=new BigInteger(null);BigInteger.ZERO.subTo(this,a);return a}function bnCompareTo(a){var b=this.s-a.s;if(0!=b)return b;var c=this.t;b=c-a.t;if(0!=b)return this.s<0?-b:b;for(;--c>=0;)if(0!=(b=this[c]-a[c]))return b;return 0}function nbits(a){var b=1,c;0!=(c=a>>>16)&&(a=c,b+=16);0!=(c=a>>8)&&(a=c,b+=8);0!=(c=a>>4)&&(a=c,b+=4);0!=(c=a>>2)&&(a=c,b+=2);0!=(c=a>>1)&&(a=c,b+=1);return b}function bnBitLength(){return this.t<=0?0:this.DB*(this.t-1)+nbits(this[this.t-1]^this.s&this.DM)}function bnpDLShiftTo(a,b){for(var c=this.t-1;c>=0;--c)b[c+a]=this[c];for(c=a-1;c>=0;--c)b[c]=0;b.t=this.t+a;b.s=this.s}function bnpDRShiftTo(a,b){for(var c=a;c<this.t;++c)b[c-a]=this[c];b.t=Math.max(this.t-a,0);b.s=this.s}function bnpLShiftTo(a,b){var c=a%this.DB,d=this.DB-c,e=(1<<d)-1,f=Math.floor(a/this.DB),g=this.s<<c&this.DM,h;for(h=this.t-1;h>=0;--h)b[h+f+1]=this[h]>>d|g,g=(this[h]&e)<<c;for(h=f-1;h>=0;--h)b[h]=0;b[f]=g;b.t=this.t+f+1;b.s=this.s;b.clamp()}function bnpRShiftTo(a,b){b.s=this.s;var c=Math.floor(a/this.DB);if(c>=this.t)b.t=0;else{var d=a%this.DB,e=this.DB-d,f=(1<<d)-1;b[0]=this[c]>>d;for(var g=c+1;g<this.t;++g)b[g-c-1]|=(this[g]&f)<<e,b[g-c]=this[g]>>d;d>0&&(b[this.t-c-1]|=(this.s&f)<<e);b.t=this.t-c;b.clamp()}}function bnpSubTo(a,b){for(var c=0,d=0,e=Math.min(a.t,this.t);c<e;)d+=this[c]-a[c],b[c++]=d&this.DM,d>>=this.DB;if(a.t<this.t){d-=a.s;for(;c<this.t;)d+=this[c],b[c++]=d&this.DM,d>>=this.DB;d+=this.s}else{d+=this.s;for(;c<a.t;)d-=a[c],b[c++]=d&this.DM,d>>=this.DB;d-=a.s}b.s=d<0?-1:0;d<-1?b[c++]=this.DV+d:d>0&&(b[c++]=d);b.t=c;b.clamp()}function bnpMultiplyTo(a,b){var c=this.abs(),d=a.abs(),e=c.t;b.t=e+d.t;for(;--e>=0;)b[e]=0;for(e=0;e<d.t;++e)b[e+c.t]=c.am(0,d[e],b,e,0,c.t);b.s=0;b.clamp();this.s!=a.s&&BigInteger.ZERO.subTo(b,b)}function bnpSquareTo(a){for(var b=this.abs(),c=a.t=2*b.t;--c>=0;)a[c]=0;for(c=0;c<b.t-1;++c){var d=b.am(c,b[c],a,2*c,0,1);(a[c+b.t]+=b.am(c+1,2*b[c],a,2*c+1,d,b.t-c-1))>=b.DV&&(a[c+b.t]-=b.DV,a[c+b.t+1]=1)}a.t>0&&(a[a.t-1]+=b.am(c,b[c],a,2*c,0,1));a.s=0;a.clamp()}function bnpDivRemTo(a,b,c){var d=a.abs();if(!(d.t<=0)){var e=this.abs();if(e.t<d.t)null!=b&&b.fromInt(0),null!=c&&this.copyTo(c);else{null==c&&(c=new BigInteger(null));var f=new BigInteger(null),g=this.s,h=a.s,i=this.DB-nbits(d[d.t-1]);i>0?(d.lShiftTo(i,f),e.lShiftTo(i,c)):(d.copyTo(f),e.copyTo(c));var j=f.t,k=f[j-1];if(0!=k){var l=k*(1<<this.F1)+(j>1?f[j-2]>>this.F2:0),m=this.FV/l,n=(1<<this.F1)/l,o=1<<this.F2,p=c.t,q=p-j,r=null==b?new BigInteger(null):b;f.dlShiftTo(q,r);c.compareTo(r)>=0&&(c[c.t++]=1,c.subTo(r,c));nbv(1).dlShiftTo(j,r);for(r.subTo(f,f);f.t<j;)f[f.t++]=0;for(;--q>=0;){var s=c[--p]==k?this.DM:Math.floor(c[p]*m+(c[p-1]+o)*n);if((c[p]+=f.am(0,s,c,q,0,j))<s){f.dlShiftTo(q,r);c.subTo(r,c);for(;c[p]<--s;)c.subTo(r,c)}}null!=b&&(c.drShiftTo(j,b),g!=h&&BigInteger.ZERO.subTo(b,b));c.t=j;c.clamp();i>0&&c.rShiftTo(i,c);g<0&&BigInteger.ZERO.subTo(c,c)}}}}function bnMod(a){var b=new BigInteger(null);this.abs().divRemTo(a,null,b);this.s<0&&b.compareTo(BigInteger.ZERO)>0&&a.subTo(b,b);return b}function Classic(a){this.m=a}function cConvert(a){return a.s<0||a.compareTo(this.m)>=0?a.mod(this.m):a}function cRevert(a){return a}function cReduce(a){a.divRemTo(this.m,null,a)}function cMulTo(a,b,c){a.multiplyTo(b,c);this.reduce(c)}function cSqrTo(a,b){a.squareTo(b);this.reduce(b)}Classic.prototype.convert=cConvert;Classic.prototype.revert=cRevert;Classic.prototype.reduce=cReduce;Classic.prototype.mulTo=cMulTo;Classic.prototype.sqrTo=cSqrTo;function bnpInvDigit(){if(this.t<1)return 0;var a=this[0];if(0==(a&1))return 0;var b=a&3;b=b*(2-(a&15)*b)&15;b=b*(2-(a&255)*b)&255;b=b*(2-((a&65535)*b&65535))&65535;b=b*(2-a*b%this.DV)%this.DV;return b>0?this.DV-b:-b}function Montgomery(a){this.m=a;this.mp=a.invDigit();this.mpl=this.mp&32767;this.mph=this.mp>>15;this.um=(1<<a.DB-15)-1;this.mt2=2*a.t}function montConvert(a){var b=new BigInteger(null);a.abs().dlShiftTo(this.m.t,b);b.divRemTo(this.m,null,b);a.s<0&&b.compareTo(BigInteger.ZERO)>0&&this.m.subTo(b,b);return b}function montRevert(a){var b=new BigInteger(null);a.copyTo(b);this.reduce(b);return b}function montReduce(a){for(;a.t<=this.mt2;)a[a.t++]=0;for(var b=0;b<this.m.t;++b){var c=a[b]&32767,d=c*this.mpl+((c*this.mph+(a[b]>>15)*this.mpl&this.um)<<15)&a.DM;c=b+this.m.t;a[c]+=this.m.am(0,d,a,b,0,this.m.t);for(;a[c]>=a.DV;)a[c]-=a.DV,a[++c]++}a.clamp();a.drShiftTo(this.m.t,a);a.compareTo(this.m)>=0&&a.subTo(this.m,a)}function montSqrTo(a,b){a.squareTo(b);this.reduce(b)}function montMulTo(a,b,c){a.multiplyTo(b,c);this.reduce(c)}Montgomery.prototype.convert=montConvert;Montgomery.prototype.revert=montRevert;Montgomery.prototype.reduce=montReduce;Montgomery.prototype.mulTo=montMulTo;Montgomery.prototype.sqrTo=montSqrTo;function bnpIsEven(){return 0==(this.t>0?this[0]&1:this.s)}function bnpExp(a,b){if(a>4294967295||a<1)return BigInteger.ONE;var c=new BigInteger(null),d=new BigInteger(null),e=b.convert(this),f=nbits(a)-1;for(e.copyTo(c);--f>=0;)if(b.sqrTo(c,d),a&1<<f)b.mulTo(d,e,c);else{var g=c;c=d;d=g}return b.revert(c)}function bnModPowInt(a,b){var c;c=a<256||b.isEven()?new Classic(b):new Montgomery(b);return this.exp(a,c)}BigInteger.prototype.copyTo=bnpCopyTo;BigInteger.prototype.fromInt=bnpFromInt;BigInteger.prototype.fromString=bnpFromString;BigInteger.prototype.clamp=bnpClamp;BigInteger.prototype.dlShiftTo=bnpDLShiftTo;BigInteger.prototype.drShiftTo=bnpDRShiftTo;BigInteger.prototype.lShiftTo=bnpLShiftTo;BigInteger.prototype.rShiftTo=bnpRShiftTo;BigInteger.prototype.subTo=bnpSubTo;BigInteger.prototype.multiplyTo=bnpMultiplyTo;BigInteger.prototype.squareTo=bnpSquareTo;BigInteger.prototype.divRemTo=bnpDivRemTo;BigInteger.prototype.invDigit=bnpInvDigit;BigInteger.prototype.isEven=bnpIsEven;BigInteger.prototype.exp=bnpExp;BigInteger.prototype.toString=bnToString;BigInteger.prototype.negate=bnNegate;BigInteger.prototype.abs=function(){return this.s<0?this.negate():this};BigInteger.prototype.compareTo=bnCompareTo;BigInteger.prototype.bitLength=bnBitLength;BigInteger.prototype.mod=bnMod;BigInteger.prototype.modPowInt=bnModPowInt;BigInteger.ZERO=nbv(0);BigInteger.ONE=nbv(1);
|
|
BigInteger.prototype.fromNumber=function(a,b,c){if("number"==typeof b)if(a<2)this.fromInt(1);else{this.fromNumber(a,c);this.testBit(a-1)||this.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),function(a,b){return a|b},this);this.isEven()&&this.dAddOffset(1,0)}else{c=[];var d=a&7;c.length=(a>>3)+1;b.nextBytes(c);d>0?c[0]&=(1<<d)-1:c[0]=0;this.fromString(c,256)}};
|
|
BigInteger.prototype.bitwiseTo=function(a,b,c){var d,e,f=Math.min(a.t,this.t);for(d=0;d<f;++d)c[d]=b(this[d],a[d]);if(a.t<this.t){e=a.s&this.DM;for(d=f;d<this.t;++d)c[d]=b(this[d],e);c.t=this.t}else{e=this.s&this.DM;for(d=f;d<a.t;++d)c[d]=b(e,a[d]);c.t=a.t}c.s=b(this.s,a.s);c.clamp()};
|
|
BigInteger.prototype.shiftLeft=function(a){var b=new BigInteger(null);a<0?this.rShiftTo(-a,b):this.lShiftTo(a,b);return b};
|
|
BigInteger.prototype.testBit=function(a){var b=Math.floor(a/this.DB);return b>=this.t?0!=this.s:0!=(this[b]&1<<a%this.DB)};
|
|
BigInteger.prototype.dAddOffset=function(a,b){if(0!=a){for(;this.t<=b;)this[this.t++]=0;for(this[b]+=a;this[b]>=this.DV;)this[b]-=this.DV,++b>=this.t&&(this[this.t++]=0),++this[b]}};
|
|
|
|
// PKCS1 padding
|
|
function pkcs1pad2(s,n){if(n<s.length+11){log("Message too long for RSA","error");return null}var ba=[];var i=s.length-1;while(i>=0&&n>0){var c=s.charCodeAt(i--);if(c<128)ba[--n]=c;else if(c>127&&c<2048)ba[--n]=c&63|128,ba[--n]=c>>6|192;else ba[--n]=c&63|128,ba[--n]=c>>6&63|128,ba[--n]=c>>12|224}ba[--n]=0;var rng={nextBytes:function(x){for(var i=0;i<x.length;++i)x[i]=Math.floor(Math.random()*254)+1}};while(n>2){var x=0;while(x==0)rng.nextBytes([x=0]),x=Math.floor(Math.random()*254)+1;ba[--n]=x}ba[--n]=2;ba[--n]=0;return new BigInteger(ba)}
|
|
|
|
// RSAKey
|
|
function RSAKey(){this.n=null;this.e=0;this.d=null}
|
|
RSAKey.prototype.setPublic=function(N,E){if(null!=N&&null!=E&&N.length>0&&E.length>0)this.n=new BigInteger(N,16),this.e=parseInt(E,16);};
|
|
RSAKey.prototype.encrypt=function(text){var m=pkcs1pad2(text,(this.n.bitLength()+7)>>3);if(null==m)return null;var c=m.modPowInt(this.e,this.n);if(null==c)return null;var h=c.toString(16);return 0==(h.length&1)?h:"0"+h};
|
|
|
|
// ---- Scanner Logic ----
|
|
var scanResults = [];
|
|
|
|
function log(msg, cls) {
|
|
var el = document.getElementById('log');
|
|
var span = document.createElement('span');
|
|
span.className = cls || '';
|
|
span.textContent = msg + '\n';
|
|
el.appendChild(span);
|
|
el.scrollTop = el.scrollHeight;
|
|
}
|
|
|
|
function clearLog() {
|
|
document.getElementById('log').innerHTML = '';
|
|
document.getElementById('results').innerHTML = '';
|
|
scanResults = [];
|
|
}
|
|
|
|
function parseIPs() {
|
|
var raw = document.getElementById('ips').value;
|
|
return raw.split(/[\n,]+/).map(function(s){ return s.trim(); }).filter(function(s){ return s.length > 0; });
|
|
}
|
|
|
|
async function fetchWithTimeout(url, opts, timeout) {
|
|
var controller = new AbortController();
|
|
var id = setTimeout(function() { controller.abort(); }, timeout || 10000);
|
|
opts = opts || {};
|
|
opts.signal = controller.signal;
|
|
try {
|
|
var resp = await fetch(url, opts);
|
|
clearTimeout(id);
|
|
return resp;
|
|
} catch(e) {
|
|
clearTimeout(id);
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
async function scanPhone(ip, user, pass, proto, site) {
|
|
var base = proto + '://' + ip;
|
|
var result = { ip: ip, mac: '', serial: '', model: '', firmware: '', site: site, error: '' };
|
|
|
|
try {
|
|
// Step 1: Get login page for RSA keys + JSESSIONID
|
|
log(' [' + ip + '] Fetching login page...', 'info');
|
|
var loginResp = await fetchWithTimeout(base + '/');
|
|
var loginHtml = await loginResp.text();
|
|
|
|
var nMatch = loginHtml.match(/g_rsa_n\s*=\s*"([A-Fa-f0-9]+)"/);
|
|
var eMatch = loginHtml.match(/g_rsa_e\s*=\s*"([A-Fa-f0-9]+)"/);
|
|
if (!nMatch || !eMatch) {
|
|
result.error = 'No RSA keys found on login page';
|
|
log(' [' + ip + '] ' + result.error, 'error');
|
|
return result;
|
|
}
|
|
|
|
// Get JSESSIONID from cookie (may not be accessible due to httpOnly)
|
|
// Try to get it from the loginForm iframe instead
|
|
log(' [' + ip + '] Fetching loginForm for fresh keys...', 'info');
|
|
var formResp = await fetchWithTimeout(base + '/servlet?m=mod_listener&p=login&q=loginForm&Random=' + Math.random());
|
|
var formHtml = await formResp.text();
|
|
|
|
var fnMatch = formHtml.match(/g_rsa_n\s*=\s*"([A-Fa-f0-9]+)"/);
|
|
var feMatch = formHtml.match(/g_rsa_e\s*=\s*"([A-Fa-f0-9]+)"/);
|
|
var rsaN = fnMatch ? fnMatch[1] : nMatch[1];
|
|
var rsaE = feMatch ? feMatch[1] : eMatch[1];
|
|
|
|
log(' [' + ip + '] RSA N: ' + rsaN.substring(0, 30) + '...', 'info');
|
|
|
|
// Step 2: Setup encryption (same as YLForm.InitEncrypt)
|
|
var rsa = new RSAKey();
|
|
rsa.setPublic(rsaN, rsaE);
|
|
|
|
var aesKeyHex = CryptoJS.MD5(Math.random().toString()).toString();
|
|
var aesIvHex = CryptoJS.MD5(Math.random().toString()).toString();
|
|
var aesKey = CryptoJS.enc.Hex.parse(aesKeyHex);
|
|
var aesIv = CryptoJS.enc.Hex.parse(aesIvHex);
|
|
|
|
// RSA encrypt the key and IV hex strings
|
|
var rsakey = rsa.encrypt(aesKeyHex);
|
|
var rsaiv = rsa.encrypt(aesIvHex);
|
|
|
|
// Step 3: Get JSESSIONID from cookies
|
|
// Since fetch may not give us cookies due to CORS, try to read from document.cookie
|
|
// or just use empty string
|
|
var jsessionId = '';
|
|
var cookieMatch = document.cookie.match(/JSESSIONID=(\w+)/);
|
|
if (cookieMatch) jsessionId = cookieMatch[1];
|
|
|
|
// Step 4: AES encrypt the password
|
|
var plaintext = Math.random().toString() + ';' + jsessionId + ';' + pass;
|
|
var encrypted = CryptoJS.AES.encrypt(plaintext, aesKey, {
|
|
iv: aesIv,
|
|
mode: CryptoJS.mode.CBC,
|
|
padding: CryptoJS.pad.ZeroPadding
|
|
});
|
|
var pwdEncrypted = encrypted.toString(); // Base64
|
|
|
|
// Step 5: POST login
|
|
log(' [' + ip + '] Logging in...', 'info');
|
|
var postBody = 'username=' + encodeURIComponent(user) +
|
|
'&pwd=' + encodeURIComponent(pwdEncrypted) +
|
|
'&rsakey=' + encodeURIComponent(rsakey) +
|
|
'&rsaiv=' + encodeURIComponent(rsaiv);
|
|
|
|
var loginResult = await fetchWithTimeout(base + '/servlet?m=mod_listener&p=login&q=login', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
body: postBody
|
|
});
|
|
var loginText = await loginResult.text();
|
|
log(' [' + ip + '] Login response: ' + loginText.replace(/<[^>]+>/g, '').trim(), 'info');
|
|
|
|
if (loginText.indexOf('"done"') < 0 && loginText.indexOf('"DONE"') < 0) {
|
|
result.error = 'Login failed: ' + loginText.replace(/<[^>]+>/g, '').trim();
|
|
log(' [' + ip + '] ' + result.error, 'error');
|
|
return result;
|
|
}
|
|
|
|
log(' [' + ip + '] Login successful!', 'success');
|
|
|
|
// Step 6: Fetch status page
|
|
var statusResp = await fetchWithTimeout(base + '/servlet?m=mod_data&p=status-status&q=load');
|
|
var statusText = await statusResp.text();
|
|
|
|
log(' [' + ip + '] Status response (' + statusText.length + ' chars)', 'info');
|
|
|
|
// Try to parse as JSON first
|
|
try {
|
|
var jsonMatch = statusText.match(/\{[\s\S]+\}/);
|
|
if (jsonMatch) {
|
|
var data = JSON.parse(jsonMatch[0]);
|
|
log(' [' + ip + '] JSON keys: ' + Object.keys(data).join(', '), 'info');
|
|
// Try various field names
|
|
result.mac = data.MAC || data.MacAddress || data.mac || '';
|
|
result.serial = data.SerialNumber || data.Serial || data.serial || data.Machine_ID || data.MachineID || data.SN || '';
|
|
result.model = data.ModelName || data.Model || data.model || data.ProductName || '';
|
|
result.firmware = data.FirmwareVersion || data.Firmware || data.firmware || data.FWVersion || '';
|
|
}
|
|
} catch(e) {}
|
|
|
|
// Fallback: parse HTML
|
|
if (!result.serial) {
|
|
var serialMatch = statusText.match(/(?:serial\s*(?:number)?|machine\s*id|SN)\s*[:=<>\s]+([A-Za-z0-9]+)/i);
|
|
if (serialMatch) result.serial = serialMatch[1];
|
|
}
|
|
if (!result.mac) {
|
|
var macMatch = statusText.match(/([0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2})/);
|
|
if (macMatch) result.mac = macMatch[1];
|
|
}
|
|
if (!result.model) {
|
|
var modelMatch = statusText.match(/(?:SIP-)?T\d{2,3}[A-Za-z]?/i);
|
|
if (modelMatch) result.model = modelMatch[0];
|
|
}
|
|
|
|
// If still no data, save raw for debugging
|
|
if (!result.serial) {
|
|
log(' [' + ip + '] Raw status (first 500): ' + statusText.substring(0, 500), 'warn');
|
|
}
|
|
|
|
if (result.serial) {
|
|
log(' [' + ip + '] Serial: ' + result.serial, 'success');
|
|
}
|
|
log(' [' + ip + '] MAC: ' + result.mac + ' Model: ' + result.model + ' FW: ' + result.firmware, 'info');
|
|
|
|
} catch(e) {
|
|
result.error = e.message || e.toString();
|
|
log(' [' + ip + '] ERROR: ' + result.error, 'error');
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
async function startScan() {
|
|
var btn = document.getElementById('scanBtn');
|
|
btn.disabled = true;
|
|
btn.textContent = 'Scanning...';
|
|
scanResults = [];
|
|
|
|
var ips = parseIPs();
|
|
var user = document.getElementById('username').value;
|
|
var pass = document.getElementById('password').value;
|
|
var site = document.getElementById('sitename').value;
|
|
var proto = document.getElementById('protocol').value;
|
|
|
|
log('========================================', 'info');
|
|
log('Starting scan of ' + ips.length + ' phone(s)...', 'info');
|
|
log('========================================', 'info');
|
|
|
|
for (var i = 0; i < ips.length; i++) {
|
|
log('[' + (i+1) + '/' + ips.length + '] Scanning ' + ips[i] + '...', 'info');
|
|
var result = await scanPhone(ips[i], user, pass, proto, site);
|
|
scanResults.push(result);
|
|
log('', '');
|
|
}
|
|
|
|
log('========================================', 'info');
|
|
log('Scan complete. ' + scanResults.length + ' phone(s) processed.', 'success');
|
|
log('========================================', 'info');
|
|
|
|
renderResults();
|
|
btn.disabled = false;
|
|
btn.textContent = 'Scan Phones';
|
|
}
|
|
|
|
function renderResults() {
|
|
var html = '<h2>Results</h2><table><tr><th>IP</th><th>MAC</th><th>Serial (Machine ID)</th><th>Model</th><th>Firmware</th><th>Site</th><th>Status</th></tr>';
|
|
for (var i = 0; i < scanResults.length; i++) {
|
|
var r = scanResults[i];
|
|
var status = r.error ? '<span class="error">' + r.error.substring(0, 50) + '</span>' : '<span class="success">OK</span>';
|
|
html += '<tr><td>' + r.ip + '</td><td>' + r.mac + '</td><td>' + r.serial + '</td><td>' + r.model + '</td><td>' + r.firmware + '</td><td>' + r.site + '</td><td>' + status + '</td></tr>';
|
|
}
|
|
html += '</table>';
|
|
document.getElementById('results').innerHTML = html;
|
|
}
|
|
|
|
function exportCSV() {
|
|
if (scanResults.length === 0) { log('No results to export.', 'warn'); return; }
|
|
var csv = 'MAC,Serial,Model,FirmwareVersion,IP,SiteName\n';
|
|
for (var i = 0; i < scanResults.length; i++) {
|
|
var r = scanResults[i];
|
|
csv += r.mac + ',' + r.serial + ',' + r.model + ',' + r.firmware + ',' + r.ip + ',' + r.site + '\n';
|
|
}
|
|
var blob = new Blob([csv], { type: 'text/csv' });
|
|
var a = document.createElement('a');
|
|
a.href = URL.createObjectURL(blob);
|
|
a.download = 'yealink_inventory.csv';
|
|
a.click();
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|