From 0ba09a4f4cf2230d980a86245fa416727364f7ed Mon Sep 17 00:00:00 2001 From: "russell@unturf.com" Date: Tue, 31 Mar 2026 19:24:59 -0400 Subject: [PATCH] rpcs3: 1 new defect (MOAD-0004 CWE-312 room password logged verbatim) --- .../rpcs3-0004-np-room-password-cwe312.patch | 39 ++++++++ defects/rpcs3/test/Rpcs3NpPasswordTest.class | Bin 0 -> 3312 bytes defects/rpcs3/test/Rpcs3NpPasswordTest.java | 92 ++++++++++++++++++ 3 files changed, 131 insertions(+) create mode 100644 defects/rpcs3/patch/rpcs3-0004-np-room-password-cwe312.patch create mode 100644 defects/rpcs3/test/Rpcs3NpPasswordTest.class create mode 100644 defects/rpcs3/test/Rpcs3NpPasswordTest.java diff --git a/defects/rpcs3/patch/rpcs3-0004-np-room-password-cwe312.patch b/defects/rpcs3/patch/rpcs3-0004-np-room-password-cwe312.patch new file mode 100644 index 000000000..379e0d1f3 --- /dev/null +++ b/defects/rpcs3/patch/rpcs3-0004-np-room-password-cwe312.patch @@ -0,0 +1,39 @@ +# UNDF: UNDF-2026-XXXXXXXXX +--- a/rpcs3/Emu/NP/np_structs_extra.cpp ++++ b/rpcs3/Emu/NP/np_structs_extra.cpp +@@ -121,8 +121,8 @@ namespace np + sceNp2.warning("roomPassword: *0x%x", req->roomPassword); + +- if (req->roomPassword) +- sceNp2.warning("data: %s", fmt::buf_to_hexstring(req->roomPassword->data, sizeof(req->roomPassword->data))); ++ if (req->roomPassword) ++ sceNp2.warning("data: [REDACTED %zu bytes]", sizeof(req->roomPassword->data)); + + sceNp2.warning("groupConfig: *0x%x", req->groupConfig); + +# Defect: rpcs3-0004 +# MOAD: 0004 (CWE-312 — Cleartext Storage of Sensitive Information) +# File: rpcs3/Emu/NP/np_structs_extra.cpp +# Function: print_SceNpMatching2CreateJoinRoomRequest +# Line: 124 +# +# Description: +# When a PS3 game creates or joins a password-protected online room via +# SceNpMatching2, RPCS3 logs the raw session password bytes verbatim at +# WARNING severity using buf_to_hexstring. The SceNpMatching2SessionPassword +# struct holds up to SCE_NP_MATCHING2_SESSION_PASSWORD_SIZE (8) bytes of +# opaque password material. Any logging framework that persists WARNING-level +# output — file logs, remote log aggregators, crash-dump collectors — will +# capture those bytes in cleartext (CWE-312). +# +# The JoinRoomRequest path (print_SceNpMatching2JoinRoomRequest) only logs the +# pointer address, not the data, so that path is not affected. +# +# Severity: MEDIUM +# - Requires WARNING log level to be active (on by default in debug builds) +# - Password is 8 bytes of opaque PS3 material, used for session access control +# - If logs are captured by a third-party service the password is exposed +# +# Fix: +# Replace buf_to_hexstring(req->roomPassword->data, ...) with a redacted +# placeholder that indicates the password is present but does not expose it. diff --git a/defects/rpcs3/test/Rpcs3NpPasswordTest.class b/defects/rpcs3/test/Rpcs3NpPasswordTest.class new file mode 100644 index 0000000000000000000000000000000000000000..9fa02ef8734d735de7834a8d2a56b3f9459d746b GIT binary patch literal 3312 zcmai0T~iy^8Ga60Y1gV^3t`#8m}KLam`_8ELu@dK0oyeRU~H*4GIf(hI)IF{E3X!8 z(l%`pr}+h)sdJHw-uNb+X?;N$lJWyO(?8J6q(L+2hw@f;58I1)n~tpbN@R+F2a6wo)U!n(Y% zEYQ|H*)!9)Jr+e9j_OFpa16&AfCVuLY}s*(77LO4;)8Txs4ammfH8E-lMUUb5hr>E zKNe`EDRKKqQZnmGx;tKvr&^22o;!yyjPp8PjbQ|@HI{R;?8zeO<*X|>LEZAs;5?b- z(Sx*#V+JmgRPz(5lz}m>u1!qOBp@)Zl1XY_XcD!K02Lffvw9g5IwoV7!WE{-Ct+i7 z61AkD6l_(RCSDg*p0CC56P09@ll^oq=O4;kf*m$);7yk8*3~N$Q|YT$49w8$1#^jg zP);DE_HR%!RF!B-a*Gy2m0yDGNzqSPmoB?2H`tXkY3h~D5{oHwU3L_=@wSePYPLIs zQR}8@+mkEORTXXERYgt$U&ha3xQm~&2mF(=BQG^M=>Kl1#)Q%1Fy4*9!V*g*ALdzB zjj&Q&rmUHXHCF!QEr2-|-r93OUHfxpqZyhTFEsK*hD3azr8Ji_u#~N!^ zkE>E?foRHEceCgY43UVFj^mX**DB3QZ`H|_4U_~Di`7z@BB;q-15Dwo z!gN&=4X`Zd&7fOMHsf;MEy~=e=~@p0bu(|9WP&+vo)V`FFau{BOv#K?ey5a>H-Qz}?lx$3Cv)-1Wwx2yztC~)qefh?B1O`j&J=goTiirsrn?W{s! z;63R&HB(pStgI(s@XIKEh4*#*T6N)X1WwmTm$x^ZH8~iRgLedPxob3b+)aVs z*K@0)7$pC?W#%vW|}gx(=MF8B`q~3%u~ds_d@dTzHB1MGKz-u&IT5cR6PH)yY{XY3~;hf966U z$5CyAZ>gcKajuo>hm=M*kADe0^92sg^v?G0z8)IUl3M2<(AA&RhDXAEiKzA$v}8ib z@HS$pZ6vnv{Ow1Yc(V6(-xf~(i4I!)5XXoDXk~~5PV%8S$N2)1kdz)r%N{S4u#RON zI!8e`-{K6FdE-^^n4A?#qjz@)CZJr`qd!4yz8MwmkBD8T##9CUY~S3#?uZr|2`9rz zjra$W;o*@;|33amJ^nE9|Gl@L2iU^t&*>&O!(rZ|U~Gf=9VHr|JGcaF;SD7{^hk&&h&*Z$G`L!C z_$hSl37CXt4zB-zgTyrR@EEP_TH-B#Rq+#6K*EhcW&=Nz)_{b*G87I4C)^(1_ZCjf z)q6XDD!haFCa6^Q>fPO>laWX=@+kpUomT+r3ZPyGkS4>r*sn+ z@iFQ08OHEAHJwi2+!Mm5UK`tntIK<&+%w`9ipkKU-=gI|IL4K@EcS5t zS+)Of{cm*AIxr^ZYoYfeH%hC4zf*x<4qzS#&8}~23O(4U`XKo4(Oo%e;KZmw@O5im z4(?Rl?6GWG)ebn#rY`uEEyDJz8o@7^LB}8O`(xQ3*ZuKPD&eAs*qkX#X#! CcJ>qi literal 0 HcmV?d00001 diff --git a/defects/rpcs3/test/Rpcs3NpPasswordTest.java b/defects/rpcs3/test/Rpcs3NpPasswordTest.java new file mode 100644 index 000000000..decd98235 --- /dev/null +++ b/defects/rpcs3/test/Rpcs3NpPasswordTest.java @@ -0,0 +1,92 @@ +import java.util.*; + +/** + * Unit test for RPCS3 MOAD-0004 defect. + * + * rpcs3-0004: np_structs_extra.cpp print_SceNpMatching2CreateJoinRoomRequest + * logs raw SceNpMatching2SessionPassword bytes via buf_to_hexstring + * at WARNING level (CWE-312: Cleartext Storage of Sensitive Information). + * + * The fix: replace buf_to_hexstring with a redacted placeholder so password + * material never enters our log stream. + */ +public class Rpcs3NpPasswordTest { + + // Simulates the defective log serializer: formats raw bytes as hex + static String logPassword_defective(byte[] passwordData) { + StringBuilder sb = new StringBuilder(); + for (byte b : passwordData) { + sb.append(String.format("%02x", b)); + } + return sb.toString(); // raw hex of secret bytes in log output + } + + // Simulates the fixed log serializer: redacts the password + static String logPassword_fixed(byte[] passwordData) { + return "[REDACTED " + passwordData.length + " bytes]"; + } + + // Checks whether a log entry contains any of the secret bytes + static boolean logContainsSecret(String logEntry, byte[] secret) { + // Build hex representation of secret + StringBuilder hex = new StringBuilder(); + for (byte b : secret) hex.append(String.format("%02x", b)); + return logEntry.contains(hex.toString()); + } + + public static void main(String[] args) { + int passed = 0; + int failed = 0; + + // Representative SceNpMatching2SessionPassword: 8 bytes + byte[] password = new byte[]{0x4e, 0x50, 0x33, (byte)0xDE, (byte)0xAD, (byte)0xBE, (byte)0xEF, 0x01}; + + // --- Test 1: defective path exposes raw bytes --- + { + String logEntry = logPassword_defective(password); + boolean exposesSecret = logContainsSecret(logEntry, password); + boolean ok = exposesSecret; // defective SHOULD contain the secret (confirming the defect) + System.out.println((ok ? "PASS" : "FAIL") + " rpcs3-0004 defect confirmed: raw bytes in log = \"" + logEntry + "\""); + if (ok) passed++; else failed++; + } + + // --- Test 2: fixed path does NOT expose raw bytes --- + { + String logEntry = logPassword_fixed(password); + boolean exposesSecret = logContainsSecret(logEntry, password); + boolean ok = !exposesSecret; + System.out.println((ok ? "PASS" : "FAIL") + " rpcs3-0004 fix: secret absent from log = \"" + logEntry + "\""); + if (ok) passed++; else failed++; + } + + // --- Test 3: fixed path still indicates presence of password (not null suppression) --- + { + String logEntry = logPassword_fixed(password); + boolean ok = logEntry.contains("REDACTED") && logEntry.contains("8 bytes"); + System.out.println((ok ? "PASS" : "FAIL") + " rpcs3-0004 fix: placeholder present = \"" + logEntry + "\""); + if (ok) passed++; else failed++; + } + + // --- Test 4: null/empty password handled gracefully --- + { + byte[] emptyPassword = new byte[0]; + String logEntry = logPassword_fixed(emptyPassword); + boolean ok = logEntry.contains("REDACTED") && logEntry.contains("0 bytes"); + System.out.println((ok ? "PASS" : "FAIL") + " rpcs3-0004 fix: empty password placeholder = \"" + logEntry + "\""); + if (ok) passed++; else failed++; + } + + // --- Test 5: all-zero password still redacted (not treated as absent) --- + { + byte[] zeroPassword = new byte[8]; // all zeros + String logEntry = logPassword_fixed(zeroPassword); + boolean exposesSecret = logEntry.contains("00000000"); // would be all zeros hex + boolean ok = !exposesSecret && logEntry.contains("REDACTED"); + System.out.println((ok ? "PASS" : "FAIL") + " rpcs3-0004 fix: all-zero password redacted = \"" + logEntry + "\""); + if (ok) passed++; else failed++; + } + + System.out.printf("%n%d/%d tests passed%n", passed, passed + failed); + if (failed > 0) System.exit(1); + } +}