44138
Recently, Ethereum go-ethereumTwo audit reports were made public, and the Xuanmao security team translated them immediately. This is the first "Go Ethereum Security Review" that is 2017-04-25_Geth-audit_Truesec, this audit report was completed on April 25, 2017. If you are using an older version go-ethereum, it is strongly recommended to upgrade to the latest version to avoid unnecessary losses.1.1. OverviewTrueSecIn April 2017, a code audit was conducted on Ethereum’s GO language implementation. The audit results show that the code quality is relatively high and developers have a certain level of security awareness. No serious security vulnerabilities were discovered during the audit. One of the most serious vulnerabilities is the bypass of the web browser's same-origin policy when RPC HTTP is turned on on the client side. Other identified issues do not have direct attack vectors to exploit, and the remainder of the report contains general comments and recommendations. 1.2. Table of Contents1.2.1. P2P and the Internet
1.2.2. Transaction and block processing
1.2.3. IPC and RPC interface
1.2.4. Javascript engine and API
1.2.5. EVM implementation
1.2.6. Miscellaneous
1.3. Results details1.3.1. P2P and the InternetTrueSecrightp2pThe code of the network part was audited, focusing mainly on:
TrueSecRLP decoding was also fuzzed through go-fuzz, and no node crash was found.1.3.1.1. Known issues Although 共享secretsexistencryption handshakeIt is implemented better in symmetric encryption algorithm, but due to thetwo-time-padDefects make the channel lack confidentiality. This is a known issue. (For details, refer to [https://github.com/ethereum/devp2p/issues/32](https://github.com/ethereum/devp2p/issues/32)and[https://github.com/ethereum/go-ethereum/issues/1315](https://github.com/ethereum/go-ethereum/issues/1315)). Since the current channel only transmits public blockchain data, this problem does not need to be solved for the time being. Another ongoing issue is the lack of a secure channel level (a default time-based replay protection mechanism was mentioned in the Ethereum developer discussion) 重放保护。TrueSecIt is recommended that the next version of the protocol be implemented by controlling the number of messages重放保护。1.3.1.2. Memory allocation is too large exist rlpx.go, TrueSecTwo user-controllable, excessive memory allocations were found.TrueSecNo exploitable DOS scenarios have been found, but it is recommended to properly verify them.When reading protocol messages, 16.8MBMemory of size can be allocated:func (rw *rlpxFrameRW) ReadMsg() (msg Msg, err error) { ... fsize := readInt24(headbuf) // ignore protocol type for now // read the frame content var rsize = fsize // frame size rounded up to 16 byte boundary if padding := fsize % 16; padding > 0 { rsize += 16 - padding } // TRUESEC: user-controlled allocation of 16.8MB: framebuf := make([]byte, rsize) ...}Since in the Ethereum protocol, the maximum message size is defined as 10MB,TrueSecIt is recommended that memory allocations are also defined to be the same size.exist encryption handshakeDuring the process, handshake information can be assigned65KBsize memory.func readHandshakeMsg(msg plainDecoder, plainSize int, prv *ecdsa.PrivateKey, r io.Reader) ([]byte, error) { ... // Could be EIP-8 format, try that. prefix := buf[:2] size := binary.BigEndian.Uint16(prefix) if size < uint16(plainSize) { return buf, fmt.Errorf("size underflow, need at least ...") } // TRUESEC: user-controlled allocation of 65KB: buf = append(buf, make([]byte, size-uint16(plainSize)+2)...) ...}Unless the handshake message does contain 65KBsize data,TrueSecIt is recommended to limit the size of handshake messages.1.3.2. Transaction and block processingTrueSecConducted code audits on transactions, block downloads, and block processing, focusing mainly on:
1.3.2.1. Divide by zero risk In Go, dividing by zero results in a panic. exist downloader.goofqosReduceConfidenceIn the method, whether division by zero occurs depends on the caller calling it correctly:func (d *Downloader) qosReduceConfidence() { peers := uint64(d.peers.Len()) ... // TRUESEC: no zero-check of peers here conf := atomic.LoadUint64(&d.rttConfidence) * (peers - 1) / peers ...}TrueSecNo exploit has been found that can cause node crashes, but it relies solely on the caller to ensured.peers.Len()It is unsafe to be non-zero.TrueSecIt is recommended that all non-constant dividends should be checked before division is performed.1.3.2.2. Code complexity TrueSecIt was found that the code parts for transaction and block processing are more complex than other parts of the code and are more difficult to read and audit. This part of the method is relatively larger, in fetcher.go,downloader.go andblockchain.goThere are over 200 lines of code in . Implementations of synchronization sometimes combine mutex locks and channel messages. For example, the structure DownloaderThe definition requires 60 lines of code, including 3 mutexes and 11 channels.Code that is difficult to read and understand is fertile ground for security problems. in particular ethThere are some code-intensive methods, structures, interfaces and extended mutexes and channels in the package.TrueSecIt is recommended to spend some effort to refactor and simplify the code to prevent future security issues.1.3.3. IPC and RPC interfaceTrueSecThe IPC and RPC (HTTP and Websocket) interfaces were audited, focusing on potential access control issues, from public API privilege escalation to private API (admin, debug, etc.).1.3.3.1. CORS: Allow all domains in default HTTP RPC HTTP RPCThe interface can be passedgethof--rpcParameters are enabled. This starts a web server that listens for HTTP requests on port 8545 and is accessible to anyone. Due to the possibility of potentially exposing ports (such as connecting to untrusted networks), only the public API allows HTTP RPC interfaces by default. The Same Origin Policy and the default Cross-Origin Resource Sharing (CORS) configuration restrict web browser access and limit the possibility of attacking the RPC API via XSS. allowed originsable to pass--rpccorsdomain "domain"To configure, you can also configure multiple domain names separated by commas.-- rpccorsdomain "domain1,domain2", or configure为--rpccorsdomain "*", making all domains accessible through standard web browsers. If not configured, the CORS header will not be set - and the browser will not allow cross-origin requests: Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8545/. (Reason: CORS header 'Access-Control-Allow-Origin missing').Due to missing CORS header, FirefoxCross-domain requests are prohibited.However, in commit 5e29f4b (from April 12, 2017) - the same-origin policy can be bypassed and RPC can be accessed from a web browser. The CORS configuration for HTTP RPC was changed to handle allowed originsarray of characters - rather than being transferred internally as a single-quote-delimited string.Previously, the comma-separated string was split into an array, upon instantiation corsBefore middleware (see Listing 1). with default value (in case the user does not explicitly configure any settings, such as using --rpccorsdomain) empty string, which will cause a character array to contain an empty string. exist commit 5e29f4bAfter that, the default value is an empty array, which is passed to thenewCorsHandlermiddlewarecors(See Listing 2).corsThe middleware then checksallowed originsThe length of the array (see Listing 3). If the length is 0, which in this case represents an empty array, the cors middleware will become the default and allow all fields. This problem can be solved by running geth -rpcTo reproduce, no need to specify anyallowed origins, and checkcommit 5e29f4bWith front and rearOPTIONCORS headers of the request. The second output Access-Control-Allow-OriginWorth noting.Note that this was true here even before the change. If it were not due to string splitting, the corsThere is no explanation for the input value (an array containing an empty string) being empty.This problem can be solved through the following JavaScriptCode to exploit, executed from any domain (even the local file system, i.e. invalid or null origin)var xhr = new XMLHttpRequest();xhr.open("POST", "http://localhost:8545", true);xhr.setRequestHeader("Content-Type", "application/json");xhr.onreadystatechange = function() { if (xhr.readyState == XMLHttpRequest.DONE && xhr.status == 200) { console.log("Modules: " + xhr.responseText); }}xhr.send('{"jsonrpc":"2.0","method":"rpc_modules","params":[],"id":67}')TrueSecIt is recommended that the default configuration of CORS be explicitly restricted (such asallowed originset tolocalhost, or not setting the CORS header at all), rather than relying on the outside world to choose a normal (safe) default165 func newCorsHandler(srv *Server, corsString string) http.Handler {166 var allowedOrigins []string167 for _, domain := range strings.Split(corsString, ",") {168 allowedOrigins = append(allowedOrigins, strings.TrimSpace(domain))169 }170 c := cors.New(cors.Options{171 AllowedOrigins: allowedOrigins,172 AllowedMethods: []string{"POST", "GET"},173 MaxAge: 600,174 AllowedHeaders: []string{"*"},175 })176 return c.Handler(srv)177 }Listing 1: rpc/http.go, before commit 5e29f4be935ff227bbf07a0c6e80e8809f5e0202 164 func newCorsHandler(srv *Server, allowedOrigins []string) http.Handler {165 c := cors.New(cors.Options{166 AllowedOrigins: allowedOrigins,167 AllowedMethods: []string{"POST", "GET"},168 MaxAge: 600,169 AllowedHeaders: []string{"*"},170 })171 return c.Handler(srv)172 }Listing 2: rpc/http.go, after commit 5e29f4be935ff227bbf07a0c6e80e8809f5e0202 113 // Allowed Origins114 if len(options.AllowedOrigins) == 0 {115 // Default is all origins116 c.allowedOriginsAll = true117 }Listing 3: vendor/github.com/rs/cors/cors.go $ curl -i -X OPTIONS -H "Access-Control-Request-Method: POST" -H "Access-Control-Request-Headers: content-type" -H "Origin: foobar" http://localhost:8545HTTP/1.1 200 OKVary: OriginVary: Access-Control-Request-MethodVary: Access-Control-Request-HeadersDate: Tue, 25 Apr 2017 08:49:10 GMTContent-Length: 0Content-Type: text/plain; charset=utf-8Listing 4: CORS headers before commit 5e29f4b $ curl -i -X OPTIONS -H "Access-Control-Request-Method: POST" -H "Access-Control-Request-Headers: content-type" -H "Origin: foobar" http://localhost:8545HTTP/1.1 200 OKAccess-Control-Allow-Headers: Content-TypeAccess-Control-Allow-Methods: POSTAccess-Control-Allow-Origin: foobarAccess-Control-Max-Age: 600Vary: OriginVary: Access-Control-Request-MethodVary: Access-Control-Request-HeadersDate: Tue, 25 Apr 2017 08:47:24 GMTContent-Length: 0Content-Type: text/plain; charset=utf-8Listing 5: CORS headers after commit 5e29f4b 1.3.4. JavaScript engine and APIThe JavaScript engine otto is the CLI script interface in Go Ethereum, a terminal interactive interpreter for IPC/RPC interface, and is also private debug APIpart of. Considering its limited code, it has a lower priority in auditing. 1.3.4.1. Weak random number seeds for pseudo-random number generation exist jsreWhen initializing the pseudo-random number generator, ifcrypto/rand(crypto/randIf the method fails, the random number seed will depend on the current UNIX time. In listing 6, this weak random number seed will be used for initialization math/randinstance.this PRNGis not used for any sensitive information and obviously should not be used for cryptographic securityRNG, but since users can run the script through the command line to usePRNG, it is obviously safer to make it fail rather than create a weak random number seed. from crypto/randGetting an error means there may be a problem somewhere else. Even if a secure random number seed is obtained, it should be noted in the documentation PRNGIt is not cryptographically secure.84 // randomSource returns a pseudo random value generator.85 func randomSource() *rand.Rand {86 bytes := make([]byte, 8)87 seed := time.Now().UnixNano() // 不是完全随机88 if _, err := crand.Read(bytes); err == nil {89 seed = int64(binary.LittleEndian.Uint64(bytes))90 }9192 src := rand.NewSource(seed)93 return rand.New(src)94 }Listing 6: internal/jsre/jsre.go 1.3.5. Implementation of Ethereum Virtual Machine (EVM)TrueSecThe code of the Ethereum Virtual Machine (EVM) part was audited, focusing on denial of service caused by misuse of memory allocation and IO operations. There is an EVM interpreter (runtime/fuzz.go) go-fuzzentry point, this entry point is used successfully.TrueSecIts functionality was confirmed, but no impactful vulnerabilities were discovered during the fuzzing process.1.3.5.1. Cheap memory consumption caused by abusing intPool Due to performance reasons, during the execution of the EVM, large integers will enter the integer pool. intPool(intpool.go). Since there is no limit on the size of the integer pool, use a specific opcodecombination, will result in unexpected cheap use of memory.0 JUMPDEST // 1 gas1 COINBASE // 2 gas2 ORIGIN // 2 gas3 EQ // 3 gas, puts 20 + 20 bytes on the intpool4 JUMP // 8 gas, puts 4-8 bytes on the intpoolFor example, the contract code will consume 3.33e9 units of gas (approximately worth 3300USD at the time) and allocate 10G memory to intPool. Expectation of 10GB memory allocation in Ethereum Virtual Machine gasThe cost is 1.95e14 (approximately 195,000,000USD)when intPoolproduceout of memory panicThis can lead to a denial of service attack. But the consensus algorithm is gaslimitRestrictions have been implemented to prevent this denial of service attack from occurring. But consider that an attacker might find a more effective padding intPoolway, orgaslimit targetGrowth is too rapid, etc.TrueSecStill recommended forintPoolsize limit.1.3.5.2. Fragile negative value protection in mining blocks Ethereum transfers between accounts are via core/evm.goinsideTransfermethod.func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) { db.SubBalance(sender, amount) db.AddBalance(recipient, amount)}enter amountIs a pointer to a signed type, possibly with negative reference values. a negative amountEthereum will be transferred from the payee to the transfer party, allowing the transfer party to steal Ethereum from the payee.When an unpacked transaction is received, the value of the transaction is verified to be positive. like tx_pool.go, validateTx():if tx.Value().Sign() < 0 { return ErrNegativeValue}But there is no such explicit verification during block processing.; Transactions with negative values are only implicitly p2pSerialization format (RLP) blocks, and RLP cannot decode negative values. Suppose an evil miner publishes a block with negative transactions in order to illegally obtain Ethereum. At this time, relying on a specific serialization format to provide protection seems a bit fragile. TrueSecIt is recommended to also explicitly check the value of transactions during block processing. Or use an unsigned type to force the value of the specified transaction to be positive. 1.3.6. Miscellaneous1.3.6.1. Conditional competition in mining code TrueSecUse "-race" to build flags and passGoThe race condition detection feature is built into the language to look for race conditions. exist ethash/ethash.goFound a similar one used in miningethash datasetsTimestamp related conditions race.func (ethash *Ethash) dataset(block uint64) []uint32 { epoch := block / epochLength // If we have a PoW for that epoch, use that ethash.lock.Lock() ... current.used = time.Now() // TRUESEC: race ethash.lock.Unlock() // Wait for generation finish, bump the timestamp and finalize the cache current.generate(ethash.dagdir, ethash.dagsondisk, ethash.tester) current.lock.Lock() current.used = time.Now() current.lock.Unlock()...}To remove race conditions, use current.lockA mutex protects the firstcurrent.usedsettings. TrueSecThere is no research on whether conditional competition will affect the mining of nodes.1.3.6.2. Too many third-party dependencies Go EthereumDepends on 71 third-party packages (viagovendor list +vendenumeration)Since each dependency potentially introduces new attack vectors and requires time and effort to monitor for security vulnerabilities, TrueSecIt is always recommended to keep the number of third-party packages to a minimum.71 dependencies is relatively large for any project. TrueSecIt is recommended that Ethereum developers investigate whether all dependencies are truly needed, or whether some of them can be replaced with code.1.4. Appendix1.4.1. StatementWe strive to provide accurate translations. Some parts may not be accurate, and some content is not important and has not been translated. Please refer to the original text if necessary. 1.4.2. Original addresshttps://github.com/ethereum/go-ethereum/blob/master/docs/audits/2017-04-25Geth-auditTruesec.pdf 1.4.3. Reference links
*Reference source: github, compiled and compiled by Javierlev@Xuanmao Security Team. Please indicate the source of reprint from FreeBuf.COM. ![]() |